]> glassweightruler.freedombox.rocks Git - Ventoy.git/commitdiff
initial commit
authorlongpanda <admin@ventoy.net>
Sat, 4 Apr 2020 16:07:50 +0000 (00:07 +0800)
committerlongpanda <admin@ventoy.net>
Sat, 4 Apr 2020 16:08:01 +0000 (00:08 +0800)
487 files changed:
DMSETUP/build.txt [new file with mode: 0644]
DMSETUP/dmsetup [new file with mode: 0644]
EDK2/README.txt [new file with mode: 0644]
EDK2/edk2-edk2-stable201911/MdeModulePkg/Application/Ventoy/Ventoy.c [new file with mode: 0644]
EDK2/edk2-edk2-stable201911/MdeModulePkg/Application/Ventoy/Ventoy.h [new file with mode: 0644]
EDK2/edk2-edk2-stable201911/MdeModulePkg/Application/Ventoy/Ventoy.inf [new file with mode: 0644]
EDK2/edk2-edk2-stable201911/MdeModulePkg/MdeModulePkg.dsc [new file with mode: 0644]
ExFAT/README.txt [new file with mode: 0644]
ExFAT/buidexfat.sh [new file with mode: 0644]
ExFAT/buidlibfuse.sh [new file with mode: 0644]
FUSEISO/build.sh [new file with mode: 0644]
FUSEISO/build_libfuse.sh [new file with mode: 0644]
FUSEISO/vtoy_fuse_iso.c [new file with mode: 0644]
GPLv3 [moved from LICENSE with 100% similarity]
GRUB2/README.txt [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/Makefile.core.def [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/boot/i386/pc/boot.S [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/commands/blocklist.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/disk/i386/pc/biosdisk.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/fs/fat.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/fs/iso9660.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/fs/udf.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/kern/file.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/kern/fs.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/kern/main.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/lib/cmdline.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/loader/efi/chainloader.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/normal/menu.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/ventoy/huffman.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/ventoy/huffman.h [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/ventoy/lzx.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/ventoy/lzx.h [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/ventoy/ventoy.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/ventoy/ventoy_def.h [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/ventoy/ventoy_json.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/ventoy/ventoy_linux.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/ventoy/ventoy_plugin.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/ventoy/ventoy_windows.c [new file with mode: 0644]
GRUB2/grub-2.04/grub-core/ventoy/wimboot.h [new file with mode: 0644]
GRUB2/grub-2.04/include/grub/ventoy.h [new file with mode: 0644]
IMG/cpio/sbin/init [new file with mode: 0644]
IMG/cpio/ventoy/busybox/busybox.xz [new file with mode: 0644]
IMG/cpio/ventoy/busybox/tmpsh [new file with mode: 0644]
IMG/cpio/ventoy/busybox/tmpxz [new file with mode: 0644]
IMG/cpio/ventoy/hook/alpine/udev_disk_hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/alpine/ventoy-hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/arch/ventoy-hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/debian/antix-disk.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/debian/antix-hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/debian/default-hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/debian/udev_disk_hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/debian/ventoy-hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/default/13-dm-disk.rules [new file with mode: 0644]
IMG/cpio/ventoy/hook/default/udev_disk_hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/default/ventoy-hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/gentoo/disk_hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/gentoo/ventoy-hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/kaos/udev_disk_hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/kaos/ventoy-hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/mageia/udev_disk_hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/mageia/ventoy-hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/manjaro/ventoy-hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/nixos/udev_disk_hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/nixos/ventoy-hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/pclos/disk_hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/pclos/ventoy-hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/rhel5/ventoy-hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/rhel5/ventoy-loader.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/rhel6/anaconda-repo-listen.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/rhel6/udev_disk_hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/rhel6/ventoy-hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/rhel7/ventoy-hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/slackware/udev_disk_hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/slackware/ventoy-hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/suse/udev_disk_hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/suse/ventoy-hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/tinycore/udev_disk_hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/tinycore/ventoy-hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/ventoy-hook-lib.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/ventoy-os-lib.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/xen/udev_disk_hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/hook/xen/ventoy-hook.sh [new file with mode: 0644]
IMG/cpio/ventoy/init [new file with mode: 0644]
IMG/cpio/ventoy/tool/ar [new file with mode: 0644]
IMG/cpio/ventoy/tool/inotifyd [new file with mode: 0644]
IMG/cpio/ventoy/tool/lz4cat [new file with mode: 0644]
IMG/cpio/ventoy/tool/ventoy_loader.sh [new file with mode: 0644]
IMG/cpio/ventoy/tool/vtoytool_install.sh [new file with mode: 0644]
IMG/cpio/ventoy/tool/zstdcat [new file with mode: 0644]
IMG/cpio/ventoy/ventoy.sh [new file with mode: 0644]
IMG/mkcpio.sh [new file with mode: 0644]
INSTALL/EFI/BOOT/BOOTX64.EFI [new file with mode: 0644]
INSTALL/Ventoy2Disk.exe [new file with mode: 0644]
INSTALL/Ventoy2Disk.sh [new file with mode: 0644]
INSTALL/grub/fonts/ascii.pf2 [new file with mode: 0644]
INSTALL/grub/grub.cfg [new file with mode: 0644]
INSTALL/grub/i386-pc/boot.img [new file with mode: 0644]
INSTALL/grub/i386-pc/core.img [new file with mode: 0644]
INSTALL/grub/themes/ventoy/background.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/menu_c.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/menu_e.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/menu_n.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/menu_ne.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/menu_nw.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/menu_s.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/menu_se.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/menu_sw.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/menu_w.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/select_c.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/slider_c.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/slider_n.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/slider_s.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/terminal_box_c.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/terminal_box_e.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/terminal_box_n.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/terminal_box_ne.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/terminal_box_nw.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/terminal_box_s.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/terminal_box_se.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/terminal_box_sw.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/terminal_box_w.png [new file with mode: 0644]
INSTALL/grub/themes/ventoy/theme.txt [new file with mode: 0644]
INSTALL/grub/x86_64-efi/normal.mod [new file with mode: 0644]
INSTALL/tool/hexdump [new file with mode: 0644]
INSTALL/tool/mkexfatfs_32 [new file with mode: 0644]
INSTALL/tool/mkexfatfs_64 [new file with mode: 0644]
INSTALL/tool/mount.exfat-fuse_32 [new file with mode: 0644]
INSTALL/tool/mount.exfat-fuse_64 [new file with mode: 0644]
INSTALL/tool/ventoy_lib.sh [new file with mode: 0644]
INSTALL/tool/vtoy_gen_uuid [new file with mode: 0644]
INSTALL/tool/vtoyfat_32 [new file with mode: 0644]
INSTALL/tool/vtoyfat_64 [new file with mode: 0644]
INSTALL/tool/xzcat [new file with mode: 0644]
INSTALL/ventoy/imdisk/32/imdisk.cpl [new file with mode: 0644]
INSTALL/ventoy/imdisk/32/imdisk.exe [new file with mode: 0644]
INSTALL/ventoy/imdisk/32/imdisk.sys [new file with mode: 0644]
INSTALL/ventoy/imdisk/64/imdisk.cpl [new file with mode: 0644]
INSTALL/ventoy/imdisk/64/imdisk.exe [new file with mode: 0644]
INSTALL/ventoy/imdisk/64/imdisk.sys [new file with mode: 0644]
INSTALL/ventoy/ipxe.krn [new file with mode: 0644]
INSTALL/ventoy/iso9660_x64.efi [new file with mode: 0644]
INSTALL/ventoy/ventoy.cpio [new file with mode: 0644]
INSTALL/ventoy/ventoy_x64.efi [new file with mode: 0644]
INSTALL/ventoy/vtoyjump32.exe [new file with mode: 0644]
INSTALL/ventoy/vtoyjump64.exe [new file with mode: 0644]
IPXE/README.txt [new file with mode: 0644]
IPXE/ipxe-3fe683e/src/arch/x86/core/runtime.c [new file with mode: 0644]
IPXE/ipxe-3fe683e/src/arch/x86/core/ventoy_vdisk.c [new file with mode: 0644]
IPXE/ipxe-3fe683e/src/arch/x86/drivers/hyperv/hyperv.c [new file with mode: 0644]
IPXE/ipxe-3fe683e/src/arch/x86/drivers/xen/hvm.c [new file with mode: 0644]
IPXE/ipxe-3fe683e/src/arch/x86/interface/pcbios/hidemem.c [new file with mode: 0644]
IPXE/ipxe-3fe683e/src/arch/x86/interface/pcbios/int13.c [new file with mode: 0644]
IPXE/ipxe-3fe683e/src/arch/x86/interface/pcbios/ventoy_int13.c [new file with mode: 0644]
IPXE/ipxe-3fe683e/src/arch/x86/interface/pcbios/ventoy_int13.h [new file with mode: 0644]
IPXE/ipxe-3fe683e/src/config/settings.h [new file with mode: 0644]
IPXE/ipxe-3fe683e/src/core/device.c [new file with mode: 0644]
IPXE/ipxe-3fe683e/src/core/main.c [new file with mode: 0644]
IPXE/ipxe-3fe683e/src/core/ventoy_dummy.c [new file with mode: 0644]
IPXE/ipxe-3fe683e/src/core/vsprintf.c [new file with mode: 0644]
IPXE/ipxe-3fe683e/src/drivers/net/efi/snp.c [new file with mode: 0644]
IPXE/ipxe-3fe683e/src/include/ipxe/sanboot.h [new file with mode: 0644]
IPXE/ipxe-3fe683e/src/include/ventoy.h [new file with mode: 0644]
IPXE/ipxe-3fe683e/src/interface/efi/efi_pci.c [new file with mode: 0644]
IPXE/ipxe-3fe683e/src/net/tcp/iscsi.c [new file with mode: 0644]
IPXE/ipxe_org_code/ipxe-3fe683e.tar.bz2 [new file with mode: 0644]
License/BSD-2-Clause-Patent.txt [new file with mode: 0644]
License/BSD.txt [new file with mode: 0644]
License/MIT.txt [new file with mode: 0644]
License/gpl-2.0.txt [new file with mode: 0644]
License/gpl-3.0.txt [new file with mode: 0644]
License/license-busybox.txt [new file with mode: 0644]
License/license-device-mapper.txt [new file with mode: 0644]
License/license-edk2.txt [new file with mode: 0644]
License/license-efifs.txt [new file with mode: 0644]
License/license-exfat.txt [new file with mode: 0644]
License/license-fat-filelib.txt [new file with mode: 0644]
License/license-fatfs.txt [new file with mode: 0644]
License/license-grub2.txt [new file with mode: 0644]
License/license-imdisk.txt [new file with mode: 0644]
License/license-ipxe.txt [new file with mode: 0644]
License/license-libfuse.txt [new file with mode: 0644]
License/license-liblzma.txt [new file with mode: 0644]
License/license-lz4.txt [new file with mode: 0644]
License/license-lzo.txt [new file with mode: 0644]
License/license-rufus.txt [new file with mode: 0644]
License/license-smallz4.txt [new file with mode: 0644]
License/license-squashfs-tools.txt [new file with mode: 0644]
License/license-vblade.txt [new file with mode: 0644]
License/license-ventoy.txt [new file with mode: 0644]
License/license-wimboot.txt [new file with mode: 0644]
License/license-xzembedded.txt [new file with mode: 0644]
License/license-zlib.txt [new file with mode: 0644]
License/license-zstd.txt [new file with mode: 0644]
License/ubdl.txt [new file with mode: 0644]
SQUASHFS/SRC/build_lz4.sh [new file with mode: 0644]
SQUASHFS/SRC/build_lzma.sh [new file with mode: 0644]
SQUASHFS/SRC/build_lzo.sh [new file with mode: 0644]
SQUASHFS/SRC/build_zstd.sh [new file with mode: 0644]
SQUASHFS/SRC/liblzma-master.zip [new file with mode: 0644]
SQUASHFS/SRC/lz4-1.8.1.2.tar.gz [new file with mode: 0644]
SQUASHFS/SRC/lzo-2.08.tar.gz [new file with mode: 0644]
SQUASHFS/SRC/zstd-1.4.4.tar.gz [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/ACKNOWLEDGEMENTS [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/CHANGES [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/COPYING [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/INSTALL [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/README [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/README-4.4 [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/DONATIONS [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/PERFORMANCE.README [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-2.0 [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-2.0-AMD64 [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-2.1 [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-3.0 [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-3.1 [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-3.2 [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-3.3 [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-4.0 [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-4.1 [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-4.2 [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-4.3 [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/pseudo-file.example [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/USAGE [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/README [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/Makefile [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/inode.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/squashfs.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/squashfs2_0.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/include/linux/squashfs_fs.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/include/linux/squashfs_fs_i.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/include/linux/squashfs_fs_sb.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/Documentation/filesystems/squashfs.txt [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/Makefile [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/block.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/cache.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/dir.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/export.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/file.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/fragment.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/id.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/inode.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/namei.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs_fs.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs_fs_i.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs_fs_sb.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/super.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/symlink.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/include/linux/squashfs_fs.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/include/linux/squashfs_fs_i.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/include/linux/squashfs_fs_sb.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/Makefile [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/action.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/action.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/build.sh [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/caches-queues-lists.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/caches-queues-lists.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/compressor.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/compressor.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/error.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/fnmatch_compat.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/gzip_wrapper.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/gzip_wrapper.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/info.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/info.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/lz4_wrapper.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/lz4_wrapper.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/lzma_wrapper.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/lzma_xz_wrapper.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/lzo_wrapper.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/lzo_wrapper.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/mksquashfs.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/mksquashfs.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/process_fragments.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/process_fragments.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/progressbar.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/progressbar.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/pseudo.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/pseudo.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/read_file.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/read_fs.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/read_fs.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/read_xattrs.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/restore.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/restore.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/sort.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/sort.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/squashfs_compat.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/squashfs_fs.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/squashfs_swap.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/swap.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-1.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-123.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-2.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-3.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-34.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-4.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquashfs.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquashfs.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquashfs_info.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquashfs_info.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquashfs_xattr.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/xattr.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/xattr.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/xz_wrapper.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/xz_wrapper.h [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/zstd_wrapper.c [new file with mode: 0644]
SQUASHFS/squashfs-tools-4.4/squashfs-tools/zstd_wrapper.h [new file with mode: 0644]
VBLADE/vblade-master/.gitignore [new file with mode: 0644]
VBLADE/vblade-master/COPYING [new file with mode: 0644]
VBLADE/vblade-master/HACKING [new file with mode: 0644]
VBLADE/vblade-master/NEWS [new file with mode: 0644]
VBLADE/vblade-master/README [new file with mode: 0644]
VBLADE/vblade-master/aoe.c [new file with mode: 0644]
VBLADE/vblade-master/ata.c [new file with mode: 0644]
VBLADE/vblade-master/bpf.c [new file with mode: 0644]
VBLADE/vblade-master/build.sh [new file with mode: 0644]
VBLADE/vblade-master/config.h [new file with mode: 0644]
VBLADE/vblade-master/config/config.h.in [new file with mode: 0644]
VBLADE/vblade-master/config/u64.c [new file with mode: 0644]
VBLADE/vblade-master/contrib/README [new file with mode: 0644]
VBLADE/vblade-master/contrib/persistence/vblade-generator [new file with mode: 0644]
VBLADE/vblade-master/contrib/persistence/vblade-persistence.txt [new file with mode: 0644]
VBLADE/vblade-master/contrib/persistence/vblade.init.daemon [new file with mode: 0644]
VBLADE/vblade-master/contrib/persistence/vblade.init.generate [new file with mode: 0644]
VBLADE/vblade-master/contrib/persistence/vblade.init.in [new file with mode: 0644]
VBLADE/vblade-master/contrib/persistence/vblade.init.lsb-daemon [new file with mode: 0644]
VBLADE/vblade-master/contrib/persistence/vblade.service [new file with mode: 0644]
VBLADE/vblade-master/contrib/persistence/vblade@.service [new file with mode: 0644]
VBLADE/vblade-master/contrib/vblade-17-aio.2.README [new file with mode: 0644]
VBLADE/vblade-master/contrib/vblade-17-aio.2.diff [new file with mode: 0644]
VBLADE/vblade-master/dat.h [new file with mode: 0644]
VBLADE/vblade-master/fns.h [new file with mode: 0644]
VBLADE/vblade-master/freebsd.c [new file with mode: 0644]
VBLADE/vblade-master/linux.c [new file with mode: 0644]
VBLADE/vblade-master/linux.h [new file with mode: 0644]
VBLADE/vblade-master/makefile [new file with mode: 0644]
VBLADE/vblade-master/sparsefile [new file with mode: 0644]
VBLADE/vblade-master/vblade.8 [new file with mode: 0644]
VBLADE/vblade-master/vblade_32 [new file with mode: 0644]
VBLADE/vblade-master/vblade_64 [new file with mode: 0644]
VBLADE/vblade-master/vbladed [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk.sln [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/Language.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/Language.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/PhyDrive.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/Res/Ventoy2Disk.manifest [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/Res/ventoy.ico [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/Utility.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.aps [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.rc [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj.filters [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj.user [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/WinDialog.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/API.txt [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/COPYRIGHT.txt [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/Configuration.txt [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/History.txt [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/License.txt [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/Media [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/Media Access API.txt [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/example.c.txt [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_access.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_access.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_cache.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_cache.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_defs.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_filelib.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_filelib.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_format.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_format.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_list.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_misc.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_misc.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_opts.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_string.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_string.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_table.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_table.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_types.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_write.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_write.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/fat_io_lib/version.txt [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/ff14/LICENSE.txt [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/ff14/source/00history.txt [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/ff14/source/00readme.txt [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/ff14/source/diskio.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/ff14/source/diskio.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/ff14/source/ff.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/ff14/source/ff.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/ff14/source/ffconf.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/ff14/source/ffsystem.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/ff14/source/ffunicode.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/resource.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/COPYING [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/README [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/Documentation/xz.txt [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/include/linux/decompress/unxz.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/include/linux/xz.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/decompress_unxz.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/Kconfig [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/Makefile [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_crc32.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_crc64.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_dec_bcj.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_dec_lzma2.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_dec_stream.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_dec_syms.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_dec_test.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_lzma2.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_private.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_stream.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/scripts/xz_wrap.sh [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/userspace/Makefile [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/userspace/boottest.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/userspace/buftest.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/userspace/bytetest.c [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/userspace/xz_config.h [new file with mode: 0644]
Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/userspace/xzminidec.c [new file with mode: 0644]
VtoyTool/BabyISO/biso.c [new file with mode: 0644]
VtoyTool/BabyISO/biso.h [new file with mode: 0644]
VtoyTool/BabyISO/biso_9660.c [new file with mode: 0644]
VtoyTool/BabyISO/biso_9660.h [new file with mode: 0644]
VtoyTool/BabyISO/biso_dump.c [new file with mode: 0644]
VtoyTool/BabyISO/biso_dump.h [new file with mode: 0644]
VtoyTool/BabyISO/biso_eltorito.c [new file with mode: 0644]
VtoyTool/BabyISO/biso_eltorito.h [new file with mode: 0644]
VtoyTool/BabyISO/biso_joliet.c [new file with mode: 0644]
VtoyTool/BabyISO/biso_joliet.h [new file with mode: 0644]
VtoyTool/BabyISO/biso_list.c [new file with mode: 0644]
VtoyTool/BabyISO/biso_list.h [new file with mode: 0644]
VtoyTool/BabyISO/biso_plat.h [new file with mode: 0644]
VtoyTool/BabyISO/biso_plat_linux.c [new file with mode: 0644]
VtoyTool/BabyISO/biso_rockridge.c [new file with mode: 0644]
VtoyTool/BabyISO/biso_rockridge.h [new file with mode: 0644]
VtoyTool/BabyISO/biso_util.c [new file with mode: 0644]
VtoyTool/BabyISO/biso_util.h [new file with mode: 0644]
VtoyTool/build.sh [new file with mode: 0644]
VtoyTool/vtoydm.c [new file with mode: 0644]
VtoyTool/vtoydump.c [new file with mode: 0644]
VtoyTool/vtoyloader.c [new file with mode: 0644]
VtoyTool/vtoytool.c [new file with mode: 0644]
VtoyTool/vtoytool/00/vtoytool_32 [new file with mode: 0644]
VtoyTool/vtoytool/00/vtoytool_64 [new file with mode: 0644]
VtoyTool/vtoytool/01/vtoytool_64 [new file with mode: 0644]
vtoyfat/build.sh [new file with mode: 0644]
vtoyfat/fat_io_lib/README.txt [new file with mode: 0644]
vtoyfat/fat_io_lib/buildlib.sh [new file with mode: 0644]
vtoyfat/vtoyfat_linux.c [new file with mode: 0644]
vtoyjump/vtoyjump.sln [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/API.txt [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/COPYRIGHT.txt [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/Configuration.txt [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/History.txt [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/License.txt [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/Media [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/Media Access API.txt [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/example.c.txt [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_access.c [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_access.h [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_cache.c [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_cache.h [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_defs.h [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_filelib.c [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_filelib.h [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_format.c [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_format.h [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_list.h [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_misc.c [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_misc.h [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_opts.h [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_string.c [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_string.h [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_table.c [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_table.h [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_types.h [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_write.c [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/fat_write.h [new file with mode: 0644]
vtoyjump/vtoyjump/fat_io_lib/version.txt [new file with mode: 0644]
vtoyjump/vtoyjump/vtoyjump.c [new file with mode: 0644]
vtoyjump/vtoyjump/vtoyjump.h [new file with mode: 0644]
vtoyjump/vtoyjump/vtoyjump.vcxproj [new file with mode: 0644]
vtoyjump/vtoyjump/vtoyjump.vcxproj.filters [new file with mode: 0644]
vtoyjump/vtoyjump/vtoyjump.vcxproj.user [new file with mode: 0644]

diff --git a/DMSETUP/build.txt b/DMSETUP/build.txt
new file mode 100644 (file)
index 0000000..1c2d5ed
--- /dev/null
@@ -0,0 +1,30 @@
+Build a static linked, small dmsetup tool
+
+======== Source Code ========
+use an old version of dmsetup
+xxx/centos-vault/5.3/os/SRPMS/device-mapper-1.02.28-2.el5.src.rpm
+
+======== Build Envrioment ======== 
+build for 32bit, static linked with dietlibc
+1. install centos 6.10 i386 with CentOS-6.10-i386-bin-DVD1.iso
+2. yum install gcc kernel-devel package
+3. install dietc libc (just make && make install)
+4. export PATH=$PATH:/opt/diet/bin
+
+======== Build Step ======== 
+1. extract device mapper source code
+2. CC="diet gcc" ./configure --disable-nls  --disable-selinux --disable-shared
+3. modify include/configure.h file
+   --- delete the line with "#define malloc rpl_malloc"
+   --- add 2 defines as follow:
+       #ifndef UINT32_MAX
+       #define UINT32_MAX  (4294967295U)
+       #endif
+       
+       #ifndef UINT64_C
+       #define UINT64_C(c) c ## ULL
+       #endif
+   
+4. make
+5. strip dmsetup/dmsetup
+5. get dmsetup/dmsetup as the binary file
diff --git a/DMSETUP/dmsetup b/DMSETUP/dmsetup
new file mode 100644 (file)
index 0000000..d5347ec
Binary files /dev/null and b/DMSETUP/dmsetup differ
diff --git a/EDK2/README.txt b/EDK2/README.txt
new file mode 100644 (file)
index 0000000..3d80223
--- /dev/null
@@ -0,0 +1,8 @@
+
+========== About Source Code =============
+Ventoy add an UEFI application module in MdeModulePkg, so I only put the module's source code here.
+You can download the EDK2 code from https://github.com/tianocore/edk2  and merge the code together.
+
+
+========== Build =============
+Follow the EDK2's build instructions
diff --git a/EDK2/edk2-edk2-stable201911/MdeModulePkg/Application/Ventoy/Ventoy.c b/EDK2/edk2-edk2-stable201911/MdeModulePkg/Application/Ventoy/Ventoy.c
new file mode 100644 (file)
index 0000000..d3c8216
--- /dev/null
@@ -0,0 +1,1073 @@
+/******************************************************************************
+ * Ventoy.c
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <Uefi.h>
+#include <Library/DebugLib.h>
+#include <Library/PrintLib.h>
+#include <Library/UefiLib.h>
+#include <Library/BaseMemoryLib.h>
+#include <Library/DevicePathLib.h>
+#include <Library/MemoryAllocationLib.h>
+#include <Library/UefiBootServicesTableLib.h>
+#include <Library/UefiRuntimeServicesTableLib.h>
+#include <Library/UefiApplicationEntryPoint.h>
+#include <Protocol/LoadedImage.h>
+#include <Guid/FileInfo.h>
+#include <Guid/FileSystemInfo.h>
+#include <Protocol/BlockIo.h>
+#include <Protocol/SimpleFileSystem.h>
+#include <Ventoy.h>
+
+BOOLEAN gDebugPrint = FALSE;
+BOOLEAN gLoadIsoEfi = FALSE;
+ventoy_chain_head *g_chain;
+ventoy_img_chunk *g_chunk;
+UINT32 g_img_chunk_num;
+ventoy_override_chunk *g_override_chunk;
+UINT32 g_override_chunk_num;
+ventoy_virt_chunk *g_virt_chunk;
+UINT32 g_virt_chunk_num;
+vtoy_block_data gBlockData;
+ventoy_sector_flag *g_sector_flag = NULL;
+UINT32 g_sector_flag_num = 0;
+static grub_env_get_pf grub_env_get = NULL;
+
+CONST CHAR16 gIso9660EfiDriverPath[] = ISO9660_EFI_DRIVER_PATH;
+
+/* Boot filename */
+CONST CHAR16 *gEfiBootFileName[] = 
+{
+    EFI_REMOVABLE_MEDIA_FILE_NAME,
+    L"\\EFI\\BOOT\\GRUBX64.EFI",
+    L"\\EFI\\BOOT\\BOOTx64.EFI",
+    L"\\EFI\\BOOT\\bootx64.efi",
+};
+
+/* EFI block device vendor device path GUID */
+EFI_GUID gVtoyBlockDevicePathGuid = VTOY_BLOCK_DEVICE_PATH_GUID;
+
+VOID EFIAPI VtoyDebug(IN CONST CHAR8  *Format, ...)
+{
+    VA_LIST  Marker;
+    CHAR16   Buffer[512];
+
+    VA_START (Marker, Format);
+    UnicodeVSPrintAsciiFormat(Buffer, sizeof(Buffer), Format, Marker);
+    VA_END (Marker);
+    
+    gST->ConOut->OutputString(gST->ConOut, Buffer);
+}
+
+VOID EFIAPI ventoy_clear_input(VOID)
+{
+    EFI_INPUT_KEY Key;
+    
+    gST->ConIn->Reset(gST->ConIn, FALSE);
+    while (EFI_SUCCESS == gST->ConIn->ReadKeyStroke(gST->ConIn, &Key))
+    {
+        ;
+    }
+    gST->ConIn->Reset(gST->ConIn, FALSE);
+}
+
+static void EFIAPI ventoy_dump_img_chunk(ventoy_chain_head *chain)
+{
+    UINT32 i;
+    int errcnt = 0;
+    UINT64 img_sec = 0;
+    ventoy_img_chunk *chunk;
+
+    chunk = (ventoy_img_chunk *)((char *)chain + chain->img_chunk_offset);
+
+    debug("##################### ventoy_dump_img_chunk #######################");
+
+    for (i = 0; i < chain->img_chunk_num; i++)
+    {
+        debug("%2u: [ %u - %u ] <==> [ %llu - %llu ]",
+               i, chunk[i].img_start_sector, chunk[i].img_end_sector, 
+               chunk[i].disk_start_sector, chunk[i].disk_end_sector);
+
+        if (i > 0 && (chunk[i].img_start_sector != chunk[i - 1].img_end_sector + 1))
+        {
+            errcnt++;
+        }
+
+        img_sec += chunk[i].img_end_sector - chunk[i].img_start_sector + 1;
+    }
+
+    if (errcnt == 0 && (img_sec * 2048 == g_chain->real_img_size_in_bytes))
+    {
+        debug("image chunk size check success");
+    }
+    else
+    {
+        debug("image chunk size check failed %d", errcnt);
+    }
+    
+    ventoy_debug_pause();
+}
+
+static void EFIAPI ventoy_dump_override_chunk(ventoy_chain_head *chain)
+{
+    UINT32 i;
+    ventoy_override_chunk *chunk;
+    
+    chunk = (ventoy_override_chunk *)((char *)chain + chain->override_chunk_offset);
+
+    debug("##################### ventoy_dump_override_chunk #######################");
+
+    for (i = 0; i < g_override_chunk_num; i++)
+    {
+        debug("%2u: [ %llu, %u ]", i, chunk[i].img_offset, chunk[i].override_size);
+    }
+
+    ventoy_debug_pause();
+}
+
+static void EFIAPI ventoy_dump_virt_chunk(ventoy_chain_head *chain)
+{
+    UINT32 i;
+    ventoy_virt_chunk *node;
+     
+    debug("##################### ventoy_dump_virt_chunk #######################");
+    debug("virt_chunk_offset=%u", chain->virt_chunk_offset);
+    debug("virt_chunk_num=%u",    chain->virt_chunk_num);
+
+    node = (ventoy_virt_chunk *)((char *)chain + chain->virt_chunk_offset);
+    for (i = 0; i < chain->virt_chunk_num; i++, node++)
+    {
+        debug("%2u: mem:[ %u, %u, %u ]  remap:[ %u, %u, %u ]", i, 
+               node->mem_sector_start,
+               node->mem_sector_end,
+               node->mem_sector_offset,
+               node->remap_sector_start,
+               node->remap_sector_end,
+               node->org_sector_start);
+    }
+    
+    ventoy_debug_pause();
+}
+
+static void EFIAPI ventoy_dump_chain(ventoy_chain_head *chain)
+{
+    UINT32 i = 0;
+    UINT8 chksum = 0;
+    UINT8 *guid;
+    
+    guid = chain->os_param.vtoy_disk_guid;
+    for (i = 0; i < sizeof(ventoy_os_param); i++)
+    {
+        chksum += *((UINT8 *)(&(chain->os_param)) + i);
+    }
+
+    debug("##################### ventoy_dump_chain #######################");
+
+    debug("os_param->chksum=0x%x (%a)", chain->os_param.chksum, chksum ? "FAILED" : "SUCCESS");
+    debug("os_param->vtoy_disk_guid=%02x%02x%02x%02x", guid[0], guid[1], guid[2], guid[3]);
+    debug("os_param->vtoy_disk_size=%llu",     chain->os_param.vtoy_disk_size);
+    debug("os_param->vtoy_disk_part_id=%u",    chain->os_param.vtoy_disk_part_id);
+    debug("os_param->vtoy_disk_part_type=%u",  chain->os_param.vtoy_disk_part_type);
+    debug("os_param->vtoy_img_path=<%a>",      chain->os_param.vtoy_img_path);
+    debug("os_param->vtoy_img_size=<%llu>",    chain->os_param.vtoy_img_size);
+    debug("os_param->vtoy_img_location_addr=<0x%llx>", chain->os_param.vtoy_img_location_addr);
+    debug("os_param->vtoy_img_location_len=<%u>",    chain->os_param.vtoy_img_location_len);
+
+    ventoy_debug_pause();
+    
+    debug("chain->disk_drive=0x%x",          chain->disk_drive);
+    debug("chain->disk_sector_size=%u",      chain->disk_sector_size);
+    debug("chain->real_img_size_in_bytes=%llu",   chain->real_img_size_in_bytes);
+    debug("chain->virt_img_size_in_bytes=%llu", chain->virt_img_size_in_bytes);
+    debug("chain->boot_catalog=%u",          chain->boot_catalog);
+    debug("chain->img_chunk_offset=%u",      chain->img_chunk_offset);
+    debug("chain->img_chunk_num=%u",         chain->img_chunk_num);
+    debug("chain->override_chunk_offset=%u", chain->override_chunk_offset);
+    debug("chain->override_chunk_num=%u",    chain->override_chunk_num);
+
+    ventoy_debug_pause();
+    
+    ventoy_dump_img_chunk(chain);
+    ventoy_dump_override_chunk(chain);
+    ventoy_dump_virt_chunk(chain);
+}
+
+EFI_HANDLE EFIAPI ventoy_get_parent_handle(IN EFI_DEVICE_PATH_PROTOCOL *pDevPath)
+{
+    EFI_HANDLE Handle = NULL;
+    EFI_STATUS Status = EFI_SUCCESS;
+    EFI_DEVICE_PATH_PROTOCOL *pLastNode = NULL;
+    EFI_DEVICE_PATH_PROTOCOL *pCurNode = NULL;
+    EFI_DEVICE_PATH_PROTOCOL *pTmpDevPath = NULL;
+    
+    pTmpDevPath = DuplicateDevicePath(pDevPath);
+    if (!pTmpDevPath)
+    {
+        return NULL;
+    }
+
+    pCurNode = pTmpDevPath;
+    while (!IsDevicePathEnd(pCurNode))
+    {
+        pLastNode = pCurNode;
+        pCurNode = NextDevicePathNode(pCurNode);
+    }
+    if (pLastNode)
+    {
+        CopyMem(pLastNode, pCurNode, sizeof(EFI_DEVICE_PATH_PROTOCOL));
+    }
+
+    pCurNode = pTmpDevPath;
+    Status = gBS->LocateDevicePath(&gEfiDevicePathProtocolGuid, &pCurNode, &Handle);
+    debug("Status:%r Parent Handle:%p DP:%s", Status, Handle, ConvertDevicePathToText(pTmpDevPath, FALSE, FALSE));
+
+    FreePool(pTmpDevPath);
+
+    return Handle;
+}
+
+EFI_STATUS EFIAPI ventoy_block_io_reset 
+(
+    IN EFI_BLOCK_IO_PROTOCOL          *This,
+    IN BOOLEAN                        ExtendedVerification
+) 
+{
+    (VOID)This;
+    (VOID)ExtendedVerification;
+       return EFI_SUCCESS;
+}
+
+STATIC EFI_STATUS EFIAPI ventoy_read_iso_sector
+(
+    IN UINT64                 Sector,
+    IN UINTN                  Count,
+    OUT VOID                 *Buffer
+)
+{
+    EFI_STATUS Status = EFI_SUCCESS;
+    EFI_LBA MapLba = 0;
+    UINT32 i = 0;
+    UINTN secLeft = 0;
+    UINTN secRead = 0;
+    UINT64 ReadStart = 0;
+    UINT64 ReadEnd = 0;
+    UINT64 OverrideStart = 0;
+    UINT64 OverrideEnd= 0;
+    UINT8 *pCurBuf = (UINT8 *)Buffer;
+    ventoy_img_chunk *pchunk = g_chunk;
+    ventoy_override_chunk *pOverride = g_override_chunk;
+    EFI_BLOCK_IO_PROTOCOL *pRawBlockIo = gBlockData.pRawBlockIo;
+
+    debug("read iso sector %lu  count %u", Sector, Count);
+
+    ReadStart = Sector * 2048;
+    ReadEnd = (Sector + Count) * 2048;
+
+    for (i = 0; Count > 0 && i < g_img_chunk_num; i++, pchunk++)
+    {
+        if (Sector >= pchunk->img_start_sector && Sector <= pchunk->img_end_sector)
+        {
+            if (g_chain->disk_sector_size == 512)
+            {
+                MapLba = (Sector - pchunk->img_start_sector) * 4 + pchunk->disk_start_sector;
+            }
+            else
+            {
+                MapLba = (Sector - pchunk->img_start_sector) * 2048 / g_chain->disk_sector_size + pchunk->disk_start_sector;
+            }
+
+            secLeft = pchunk->img_end_sector + 1 - Sector;
+            secRead = (Count < secLeft) ? Count : secLeft;
+
+            Status = pRawBlockIo->ReadBlocks(pRawBlockIo, pRawBlockIo->Media->MediaId,
+                                     MapLba, secRead * 2048, pCurBuf);
+            if (EFI_ERROR(Status))
+            {
+                debug("Raw disk read block failed %r", Status);
+                return Status;
+            }
+
+            Count -= secRead;
+            Sector += secRead;
+            pCurBuf += secRead * 2048;
+        }
+    }
+
+    if (ReadStart > g_chain->real_img_size_in_bytes)
+    {
+        return EFI_SUCCESS;
+    }
+
+    /* override data */
+    pCurBuf = (UINT8 *)Buffer;
+    for (i = 0; i < g_override_chunk_num; i++, pOverride++)
+    {
+        OverrideStart = pOverride->img_offset;
+        OverrideEnd = pOverride->img_offset + pOverride->override_size;
+    
+        if (OverrideStart >= ReadEnd || ReadStart >= OverrideEnd)
+        {
+            continue;
+        }
+
+        if (ReadStart <= OverrideStart)
+        {
+            if (ReadEnd <= OverrideEnd)
+            {
+                CopyMem(pCurBuf + OverrideStart - ReadStart, pOverride->override_data, ReadEnd - OverrideStart);  
+            }
+            else
+            {
+                CopyMem(pCurBuf + OverrideStart - ReadStart, pOverride->override_data, pOverride->override_size);
+            }
+        }
+        else
+        {
+            if (ReadEnd <= OverrideEnd)
+            {
+                CopyMem(pCurBuf, pOverride->override_data + ReadStart - OverrideStart, ReadEnd - ReadStart);     
+            }
+            else
+            {
+                CopyMem(pCurBuf, pOverride->override_data + ReadStart - OverrideStart, OverrideEnd - ReadStart);
+            }
+        }
+    }
+
+    return EFI_SUCCESS;    
+}
+
+EFI_STATUS EFIAPI ventoy_block_io_read 
+(
+    IN EFI_BLOCK_IO_PROTOCOL          *This,
+    IN UINT32                          MediaId,
+    IN EFI_LBA                         Lba,
+    IN UINTN                           BufferSize,
+    OUT VOID                          *Buffer
+) 
+{
+    UINT32 i = 0;
+    UINT32 j = 0;
+    UINT32 lbacount = 0;
+    UINT32 secNum = 0;
+    UINT64 offset = 0;
+    EFI_LBA curlba = 0;
+    EFI_LBA lastlba = 0;
+    UINT8 *lastbuffer;
+    ventoy_sector_flag *cur_flag;
+    ventoy_virt_chunk *node;
+    
+    //debug("### ventoy_block_io_read sector:%u count:%u", (UINT32)Lba, (UINT32)BufferSize / 2048);
+
+    secNum = BufferSize / 2048;
+    offset = Lba * 2048;
+
+    if (offset + BufferSize < g_chain->real_img_size_in_bytes)
+    {
+        return ventoy_read_iso_sector(Lba, secNum, Buffer);
+    }
+
+    if (secNum > g_sector_flag_num)
+    {
+        cur_flag = AllocatePool(secNum * sizeof(ventoy_sector_flag));
+        if (NULL == cur_flag)
+        {
+            return EFI_OUT_OF_RESOURCES;
+        }
+
+        FreePool(g_sector_flag);
+        g_sector_flag = cur_flag;
+        g_sector_flag_num = secNum;
+    }
+
+    for (curlba = Lba, cur_flag = g_sector_flag, j = 0; j < secNum; j++, curlba++, cur_flag++)
+    {
+        cur_flag->flag = 0;
+        for (node = g_virt_chunk, i = 0; i < g_virt_chunk_num; i++, node++)
+        {
+            if (curlba >= node->mem_sector_start && curlba < node->mem_sector_end)
+            {
+                CopyMem((UINT8 *)Buffer + j * 2048, 
+                       (char *)g_virt_chunk + node->mem_sector_offset + (curlba - node->mem_sector_start) * 2048,
+                       2048);
+                cur_flag->flag = 1;
+                break;
+            }
+            else if (curlba >= node->remap_sector_start && curlba < node->remap_sector_end)
+            {
+                cur_flag->remap_lba = node->org_sector_start + curlba - node->remap_sector_start;
+                cur_flag->flag = 2;
+                break;
+            }
+        }
+    }
+
+    for (curlba = Lba, cur_flag = g_sector_flag, j = 0; j < secNum; j++, curlba++, cur_flag++)
+    {
+        if (cur_flag->flag == 2)
+        {
+            if (lastlba == 0)
+            {
+                lastbuffer = (UINT8 *)Buffer + j * 2048;
+                lastlba = cur_flag->remap_lba;
+                lbacount = 1;
+            }
+            else if (lastlba + lbacount == cur_flag->remap_lba)
+            {
+                lbacount++;
+            }
+            else
+            {
+                ventoy_read_iso_sector(lastlba, lbacount, lastbuffer);
+                lastbuffer = (UINT8 *)Buffer + j * 2048;
+                lastlba = cur_flag->remap_lba;
+                lbacount = 1;
+            }
+        }
+    }
+
+    if (lbacount > 0)
+    {
+        ventoy_read_iso_sector(lastlba, lbacount, lastbuffer);
+    }
+
+       return EFI_SUCCESS;
+}
+
+EFI_STATUS EFIAPI ventoy_block_io_write 
+(
+    IN EFI_BLOCK_IO_PROTOCOL          *This,
+    IN UINT32                          MediaId,
+    IN EFI_LBA                         Lba,
+    IN UINTN                           BufferSize,
+    IN VOID                           *Buffer
+) 
+{
+    (VOID)This;
+    (VOID)MediaId;
+    (VOID)Lba;
+    (VOID)BufferSize;
+    (VOID)Buffer;
+       return EFI_WRITE_PROTECTED;
+}
+
+EFI_STATUS EFIAPI ventoy_block_io_flush(IN EFI_BLOCK_IO_PROTOCOL *This)
+{
+       (VOID)This;
+       return EFI_SUCCESS;
+}
+
+
+EFI_STATUS EFIAPI ventoy_fill_device_path(VOID)
+{
+    UINTN NameLen = 0;
+    UINT8 TmpBuf[128] = {0};
+    VENDOR_DEVICE_PATH *venPath = NULL;
+
+    venPath = (VENDOR_DEVICE_PATH *)TmpBuf;
+    NameLen = StrSize(VTOY_BLOCK_DEVICE_PATH_NAME);
+    venPath->Header.Type = HARDWARE_DEVICE_PATH;
+    venPath->Header.SubType = HW_VENDOR_DP;
+    venPath->Header.Length[0] = sizeof(VENDOR_DEVICE_PATH) + NameLen;
+    venPath->Header.Length[1] = 0;
+    CopyMem(&venPath->Guid, &gVtoyBlockDevicePathGuid, sizeof(EFI_GUID));
+    CopyMem(venPath + 1, VTOY_BLOCK_DEVICE_PATH_NAME, NameLen);
+    
+    gBlockData.Path = AppendDevicePathNode(NULL, (EFI_DEVICE_PATH_PROTOCOL *)TmpBuf);
+    gBlockData.DevicePathCompareLen = sizeof(VENDOR_DEVICE_PATH) + NameLen;
+
+    debug("gBlockData.Path=<%s>\n", ConvertDevicePathToText(gBlockData.Path, FALSE, FALSE));
+
+    return EFI_SUCCESS;
+}
+
+EFI_STATUS EFIAPI ventoy_set_variable(VOID)
+{
+    EFI_STATUS Status = EFI_SUCCESS;
+    EFI_GUID VarGuid = VENTOY_GUID;
+    
+    Status = gRT->SetVariable(L"VentoyOsParam", &VarGuid, 
+                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+                  sizeof(g_chain->os_param), &(g_chain->os_param));
+    debug("set efi variable %r", Status);
+
+    return Status;
+}
+
+EFI_STATUS EFIAPI ventoy_delete_variable(VOID)
+{
+    EFI_STATUS Status = EFI_SUCCESS;
+    EFI_GUID VarGuid = VENTOY_GUID;
+    
+    Status = gRT->SetVariable(L"VentoyOsParam", &VarGuid, 
+                  EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+                  0, NULL);
+    debug("delete efi variable %r", Status);
+
+    return Status;
+}
+
+
+EFI_STATUS EFIAPI ventoy_install_blockio(IN EFI_HANDLE ImageHandle)
+{   
+    EFI_STATUS Status = EFI_SUCCESS;
+    EFI_BLOCK_IO_PROTOCOL *pBlockIo = &(gBlockData.BlockIo);
+    
+    ventoy_fill_device_path();
+    
+    gBlockData.Media.BlockSize = 2048;
+    gBlockData.Media.LastBlock = g_chain->virt_img_size_in_bytes / 2048 - 1;
+    gBlockData.Media.ReadOnly = TRUE;
+    gBlockData.Media.MediaPresent = 1;
+    gBlockData.Media.LogicalBlocksPerPhysicalBlock = 1;
+
+       pBlockIo->Revision = EFI_BLOCK_IO_PROTOCOL_REVISION3;
+       pBlockIo->Media = &(gBlockData.Media);
+       pBlockIo->Reset = ventoy_block_io_reset;
+       pBlockIo->ReadBlocks = ventoy_block_io_read;
+       pBlockIo->WriteBlocks = ventoy_block_io_write;
+       pBlockIo->FlushBlocks = ventoy_block_io_flush;
+
+    Status = gBS->InstallMultipleProtocolInterfaces(&gBlockData.Handle,
+            &gEfiBlockIoProtocolGuid, &gBlockData.BlockIo,
+            &gEfiDevicePathProtocolGuid, gBlockData.Path,
+            NULL);
+
+    debug("Install protocol %r", Status);
+
+    if (EFI_ERROR(Status))
+    {
+        return Status;
+    }
+
+    Status = gBS->ConnectController(gBlockData.Handle, NULL, NULL, 1);
+    debug("Connect controller %r", Status);
+
+    return EFI_SUCCESS;
+}
+
+
+EFI_STATUS EFIAPI ventoy_load_image
+(
+    IN EFI_HANDLE ImageHandle,
+    IN EFI_DEVICE_PATH_PROTOCOL *pDevicePath,
+    IN CONST CHAR16 *FileName,
+    IN UINTN FileNameLen,
+    OUT EFI_HANDLE *Image
+)
+{
+    EFI_STATUS Status = EFI_SUCCESS;
+    CHAR16 TmpBuf[256] = {0};
+    FILEPATH_DEVICE_PATH *pFilePath = NULL;
+    EFI_DEVICE_PATH_PROTOCOL *pImgPath = NULL;
+
+    pFilePath = (FILEPATH_DEVICE_PATH *)TmpBuf;
+    pFilePath->Header.Type = MEDIA_DEVICE_PATH;
+    pFilePath->Header.SubType = MEDIA_FILEPATH_DP;
+    pFilePath->Header.Length[0] = FileNameLen + sizeof(EFI_DEVICE_PATH_PROTOCOL);
+    pFilePath->Header.Length[1] = 0;
+    CopyMem(pFilePath->PathName, FileName, FileNameLen);
+    
+    pImgPath = AppendDevicePathNode(pDevicePath, (EFI_DEVICE_PATH_PROTOCOL *)pFilePath);
+    if (!pImgPath)
+    {
+        return EFI_NOT_FOUND;
+    }
+    
+    Status = gBS->LoadImage(FALSE, ImageHandle, pImgPath, NULL, 0, Image);
+    
+    debug("Load Image File %r DP: <%s>", Status, ConvertDevicePathToText(pImgPath, FALSE, FALSE));
+
+    FreePool(pImgPath);
+    
+    return Status;
+}
+
+
+STATIC EFI_STATUS EFIAPI ventoy_find_iso_disk(IN EFI_HANDLE ImageHandle)
+{
+    UINTN i = 0;
+    UINTN Count = 0;
+    UINT64 DiskSize = 0;
+    UINT8 *pBuffer = NULL;
+    EFI_HANDLE *Handles;
+    EFI_STATUS Status = EFI_SUCCESS;
+    EFI_BLOCK_IO_PROTOCOL *pBlockIo;
+
+    pBuffer = AllocatePool(2048);
+    if (!pBuffer)
+    {
+        return EFI_OUT_OF_RESOURCES;
+    }
+
+    Status = gBS->LocateHandleBuffer(ByProtocol, &gEfiBlockIoProtocolGuid, 
+                                     NULL, &Count, &Handles);
+    if (EFI_ERROR(Status))
+    {
+        FreePool(pBuffer);
+        return Status;
+    }
+
+    for (i = 0; i < Count; i++)
+    {
+        Status = gBS->HandleProtocol(Handles[i], &gEfiBlockIoProtocolGuid, (VOID **)&pBlockIo);
+        if (EFI_ERROR(Status))
+        {
+            continue;
+        }
+
+        DiskSize = (pBlockIo->Media->LastBlock + 1) * pBlockIo->Media->BlockSize;
+        debug("This Disk size: %llu", DiskSize);
+        if (g_chain->os_param.vtoy_disk_size != DiskSize)
+        {
+            continue;
+        }
+
+        Status = pBlockIo->ReadBlocks(pBlockIo, pBlockIo->Media->MediaId, 0, 512, pBuffer);
+        if (EFI_ERROR(Status))
+        {
+            debug("ReadBlocks filed %r", Status);
+            continue;
+        }
+
+        if (CompareMem(g_chain->os_param.vtoy_disk_guid, pBuffer + 0x180, 16) == 0)
+        {
+            gBlockData.RawBlockIoHandle = Handles[i];
+            gBlockData.pRawBlockIo = pBlockIo;
+            gBS->OpenProtocol(Handles[i], &gEfiDevicePathProtocolGuid, 
+                              (VOID **)&(gBlockData.pDiskDevPath),
+                              ImageHandle,
+                              Handles[i],
+                              EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+            
+            debug("Find Ventoy Disk Handle:%p DP:%s", Handles[i], 
+                ConvertDevicePathToText(gBlockData.pDiskDevPath, FALSE, FALSE));
+            break;
+        }
+    }
+
+    FreePool(Handles);
+
+    if (i >= Count)
+    {
+        return EFI_NOT_FOUND;
+    }
+    else
+    {
+        return EFI_SUCCESS;
+    }
+}
+
+STATIC EFI_STATUS EFIAPI ventoy_find_iso_disk_fs(IN EFI_HANDLE ImageHandle)
+{
+    UINTN i = 0;
+    UINTN Count = 0;
+    EFI_HANDLE Parent = NULL;
+    EFI_HANDLE *Handles = NULL;
+    EFI_STATUS Status = EFI_SUCCESS;
+    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *pFile = NULL;
+    EFI_DEVICE_PATH_PROTOCOL *pDevPath = NULL;
+
+    Status = gBS->LocateHandleBuffer(ByProtocol, &gEfiSimpleFileSystemProtocolGuid, 
+                                     NULL, &Count, &Handles);
+    if (EFI_ERROR(Status))
+    {
+        return Status;
+    }
+
+    debug("ventoy_find_iso_disk_fs fs count:%u", Count);
+
+    for (i = 0; i < Count; i++)
+    {
+        Status = gBS->HandleProtocol(Handles[i], &gEfiSimpleFileSystemProtocolGuid, (VOID **)&pFile);
+        if (EFI_ERROR(Status))
+        {
+            continue;
+        }
+
+        Status = gBS->OpenProtocol(Handles[i], &gEfiDevicePathProtocolGuid, 
+                                   (VOID **)&pDevPath,
+                                   ImageHandle,
+                                   Handles[i],
+                                   EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+        if (EFI_ERROR(Status))
+        {
+            debug("Failed to open device path protocol %r", Status);
+            continue;
+        }
+
+        debug("Handle:%p FS DP: <%s>", Handles[i], ConvertDevicePathToText(pDevPath, FALSE, FALSE));
+        Parent = ventoy_get_parent_handle(pDevPath);
+
+        if (Parent == gBlockData.RawBlockIoHandle)
+        {
+            debug("Find ventoy disk fs");
+            gBlockData.DiskFsHandle = Handles[i];
+            gBlockData.pDiskFs = pFile;
+            gBlockData.pDiskFsDevPath = pDevPath;
+            break;
+        }
+    }
+
+    FreePool(Handles);
+
+    return EFI_SUCCESS;
+}
+
+STATIC EFI_STATUS EFIAPI ventoy_load_isoefi_driver(IN EFI_HANDLE ImageHandle)
+{
+    EFI_HANDLE Image = NULL;
+    EFI_STATUS Status = EFI_SUCCESS;
+    CHAR16 LogVar[4] = L"5";
+            
+    Status = ventoy_load_image(ImageHandle, gBlockData.pDiskFsDevPath, 
+                               gIso9660EfiDriverPath, 
+                               sizeof(gIso9660EfiDriverPath), 
+                               &Image);
+    debug("load iso efi driver status:%r", Status);
+
+    if (gDebugPrint)
+    {
+        gRT->SetVariable(L"FS_LOGGING", &gShellVariableGuid, 
+                         EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+                         sizeof(LogVar), LogVar);
+    }
+
+    gRT->SetVariable(L"FS_NAME_NOCASE", &gShellVariableGuid, 
+                     EFI_VARIABLE_BOOTSERVICE_ACCESS | EFI_VARIABLE_RUNTIME_ACCESS,
+                     sizeof(LogVar), LogVar);
+
+    gBlockData.IsoDriverImage = Image;
+    Status = gBS->StartImage(Image, NULL, NULL);
+    debug("Start iso efi driver status:%r", Status);
+
+    return EFI_SUCCESS;
+}
+
+static int ventoy_update_image_location(ventoy_os_param *param)
+{
+    EFI_STATUS Status = EFI_SUCCESS;
+    UINT8 chksum = 0;
+    unsigned int i;
+    unsigned int length;
+    UINTN address = 0;
+    void *buffer = NULL;
+    ventoy_image_location *location = NULL;
+    ventoy_image_disk_region *region = NULL;
+    ventoy_img_chunk *chunk = g_chunk;
+
+    length = sizeof(ventoy_image_location) + (g_img_chunk_num - 1) * sizeof(ventoy_image_disk_region);
+
+    Status = gBS->AllocatePool(EfiRuntimeServicesData, length + 4096 * 2, &buffer);
+    if (EFI_ERROR(Status) || NULL == buffer)
+    {
+        debug("Failed to allocate runtime pool %r\n", Status);
+        return 1;
+    }
+
+    address = (UINTN)buffer;
+
+    if (address % 4096)
+    {
+        address += 4096 - (address % 4096);
+    }
+
+    param->chksum = 0;
+    param->vtoy_img_location_addr = address;
+    param->vtoy_img_location_len = length;
+
+    /* update check sum */
+    for (i = 0; i < sizeof(ventoy_os_param); i++)
+    {
+        chksum += *((UINT8 *)param + i);
+    }
+    param->chksum = (chksum == 0) ? 0 : (UINT8)(0x100 - chksum);
+
+    location = (ventoy_image_location *)(unsigned long)(param->vtoy_img_location_addr);
+    if (NULL == location)
+    {
+        return 0;
+    }
+
+    CopyMem(&location->guid, &param->guid, sizeof(ventoy_guid));
+    location->image_sector_size = 2048;
+    location->disk_sector_size  = g_chain->disk_sector_size;
+    location->region_count = g_img_chunk_num;
+
+    region = location->regions;
+
+    for (i = 0; i < g_img_chunk_num; i++)
+    {
+        region->image_sector_count = chunk->img_end_sector - chunk->img_start_sector + 1;
+        region->image_start_sector = chunk->img_start_sector;
+        region->disk_start_sector  = chunk->disk_start_sector;
+        region++;
+        chunk++;
+    }
+
+    return 0;
+}
+
+STATIC EFI_STATUS EFIAPI ventoy_parse_cmdline(IN EFI_HANDLE ImageHandle)
+{   
+    UINT32 i = 0;
+    UINT8 chksum = 0;
+    CHAR16 *pPos = NULL;
+    CHAR16 *pCmdLine = NULL;
+    EFI_STATUS Status = EFI_SUCCESS;
+    ventoy_grub_param *pGrubParam = NULL;
+    EFI_LOADED_IMAGE_PROTOCOL *pImageInfo = NULL;
+
+    Status = gBS->HandleProtocol(ImageHandle, &gEfiLoadedImageProtocolGuid, (VOID **)&pImageInfo);
+    if (EFI_ERROR(Status))
+    {
+        VtoyDebug("Failed to handle load image protocol %r", Status);
+        return Status;
+    }
+
+    pCmdLine = (CHAR16 *)AllocatePool(pImageInfo->LoadOptionsSize + 4);
+    SetMem(pCmdLine, pImageInfo->LoadOptionsSize + 4, 0);
+    CopyMem(pCmdLine, pImageInfo->LoadOptions, pImageInfo->LoadOptionsSize);
+
+    if (StrStr(pCmdLine, L"debug"))
+    {
+        gDebugPrint = TRUE;
+    }
+
+    if (StrStr(pCmdLine, L"isoefi=on"))
+    {
+        gLoadIsoEfi = TRUE;
+    }
+
+    debug("cmdline:<%s>", pCmdLine);
+
+    pPos = StrStr(pCmdLine, L"env_param=");
+    if (!pPos)
+    {
+        return EFI_INVALID_PARAMETER;
+    }
+    
+    pGrubParam = (ventoy_grub_param *)StrHexToUintn(pPos + StrLen(L"env_param="));
+    grub_env_get = pGrubParam->grub_env_get;
+
+
+    pPos = StrStr(pCmdLine, L"mem:");
+    g_chain = (ventoy_chain_head *)StrHexToUintn(pPos + 4);
+
+    g_chunk = (ventoy_img_chunk *)((char *)g_chain + g_chain->img_chunk_offset);
+    g_img_chunk_num = g_chain->img_chunk_num;
+    g_override_chunk = (ventoy_override_chunk *)((char *)g_chain + g_chain->override_chunk_offset);
+    g_override_chunk_num = g_chain->override_chunk_num;
+    g_virt_chunk = (ventoy_virt_chunk *)((char *)g_chain + g_chain->virt_chunk_offset);
+    g_virt_chunk_num = g_chain->virt_chunk_num;
+
+    for (i = 0; i < sizeof(ventoy_os_param); i++)
+    {
+        chksum += *((UINT8 *)(&(g_chain->os_param)) + i);
+    }
+
+    if (gDebugPrint)
+    {
+        debug("os param checksum: 0x%x %a", g_chain->os_param.chksum, chksum ? "FAILED" : "SUCCESS");
+    }
+
+    ventoy_update_image_location(&(g_chain->os_param));
+
+    if (gDebugPrint)
+    {
+        ventoy_dump_chain(g_chain);
+    }
+
+    FreePool(pCmdLine);
+    return EFI_SUCCESS;
+}
+
+EFI_STATUS EFIAPI ventoy_boot(IN EFI_HANDLE ImageHandle)
+{
+    UINTN i = 0;
+    UINTN j = 0;
+    UINTN Find = 0;
+    UINTN Count = 0;
+    EFI_HANDLE Image = NULL;
+    EFI_HANDLE *Handles = NULL;
+    EFI_STATUS Status = EFI_SUCCESS;
+    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *pFile = NULL;
+    EFI_DEVICE_PATH_PROTOCOL *pDevPath = NULL;
+
+    Status = gBS->LocateHandleBuffer(ByProtocol, &gEfiSimpleFileSystemProtocolGuid, 
+                                     NULL, &Count, &Handles);
+    if (EFI_ERROR(Status))
+    {
+        return Status;
+    }
+
+    debug("ventoy_boot fs count:%u", Count);
+
+    for (i = 0; i < Count; i++)
+    {
+        Status = gBS->HandleProtocol(Handles[i], &gEfiSimpleFileSystemProtocolGuid, (VOID **)&pFile);
+        if (EFI_ERROR(Status))
+        {
+            continue;
+        }
+
+        Status = gBS->OpenProtocol(Handles[i], &gEfiDevicePathProtocolGuid, 
+                                   (VOID **)&pDevPath,
+                                   ImageHandle,
+                                   Handles[i],
+                                   EFI_OPEN_PROTOCOL_GET_PROTOCOL);
+        if (EFI_ERROR(Status))
+        {
+            debug("Failed to open device path protocol %r", Status);
+            continue;
+        }
+
+        debug("Handle:%p FS DP: <%s>", Handles[i], ConvertDevicePathToText(pDevPath, FALSE, FALSE));
+        if (CompareMem(gBlockData.Path, pDevPath, gBlockData.DevicePathCompareLen))
+        {
+            debug("Not ventoy disk file system");
+            continue;
+        }
+
+        for (j = 0; j < ARRAY_SIZE(gEfiBootFileName); j++)
+        {
+            Status = ventoy_load_image(ImageHandle, pDevPath, gEfiBootFileName[j], 
+                                       StrSize(gEfiBootFileName[j]), &Image);
+            if (EFI_SUCCESS == Status)
+            {
+                break;
+            }
+            debug("Failed to load image %r <%s>", Status, gEfiBootFileName[j]);
+        }
+
+        if (j >= ARRAY_SIZE(gEfiBootFileName))
+        {
+            continue;
+        }
+
+        Find++;
+        debug("Find boot file, now try to boot .....");
+        ventoy_debug_pause();
+
+        if (gDebugPrint)
+        {
+            gST->ConIn->Reset(gST->ConIn, FALSE);
+        }
+        
+        Status = gBS->StartImage(Image, NULL, NULL);
+        if (EFI_ERROR(Status))
+        {
+            debug("Failed to start image %r", Status);
+            gBS->UnloadImage(Image);
+            break;
+        }
+    }
+
+    FreePool(Handles);
+
+    if (Find == 0)
+    {
+        return EFI_NOT_FOUND;
+    }
+
+    return EFI_SUCCESS;
+}
+
+EFI_STATUS EFIAPI ventoy_clean_env(VOID)
+{
+    FreePool(g_sector_flag);
+    g_sector_flag_num = 0;
+
+    if (gLoadIsoEfi && gBlockData.IsoDriverImage)
+    {
+        gBS->UnloadImage(gBlockData.IsoDriverImage);
+    }
+
+    gBS->DisconnectController(gBlockData.Handle, NULL, NULL);
+
+    gBS->UninstallMultipleProtocolInterfaces(gBlockData.Handle,
+            &gEfiBlockIoProtocolGuid, &gBlockData.BlockIo,
+            &gEfiDevicePathProtocolGuid, gBlockData.Path,
+            NULL);
+
+    ventoy_delete_variable();
+
+    if (g_chain->os_param.vtoy_img_location_addr)
+    {
+        FreePool((VOID *)(UINTN)g_chain->os_param.vtoy_img_location_addr);
+    }
+
+    return EFI_SUCCESS;
+}
+
+EFI_STATUS EFIAPI VentoyEfiMain
+
+(
+    IN EFI_HANDLE         ImageHandle,
+    IN EFI_SYSTEM_TABLE  *SystemTable
+)
+{
+    EFI_STATUS Status = EFI_SUCCESS;
+    
+    g_sector_flag_num = 512; /* initial value */
+
+    g_sector_flag = AllocatePool(g_sector_flag_num * sizeof(ventoy_sector_flag));
+    if (NULL == g_sector_flag)
+    {
+        return EFI_OUT_OF_RESOURCES;
+    }
+
+    gST->ConOut->ClearScreen(gST->ConOut);
+    ventoy_clear_input();
+
+    ventoy_parse_cmdline(ImageHandle);
+    ventoy_set_variable();
+    ventoy_find_iso_disk(ImageHandle);
+
+    if (gLoadIsoEfi)
+    {
+        ventoy_find_iso_disk_fs(ImageHandle);
+        ventoy_load_isoefi_driver(ImageHandle);
+    }
+
+    ventoy_debug_pause();
+    
+    ventoy_install_blockio(ImageHandle);
+
+    ventoy_debug_pause();
+
+    Status = ventoy_boot(ImageHandle);
+    if (EFI_NOT_FOUND == Status)
+    {
+        gST->ConOut->OutputString(gST->ConOut, L"No bootfile found for UEFI!\r\n");
+        gST->ConOut->OutputString(gST->ConOut, L"Maybe the image does not support " VENTOY_UEFI_DESC  L"!\r\n");
+        sleep(300);
+    }
+
+    ventoy_clean_env();
+
+    ventoy_clear_input();
+    gST->ConOut->ClearScreen(gST->ConOut);
+
+    
+
+    return EFI_SUCCESS;
+}
+
+
diff --git a/EDK2/edk2-edk2-stable201911/MdeModulePkg/Application/Ventoy/Ventoy.h b/EDK2/edk2-edk2-stable201911/MdeModulePkg/Application/Ventoy/Ventoy.h
new file mode 100644 (file)
index 0000000..988421c
--- /dev/null
@@ -0,0 +1,233 @@
+/******************************************************************************
+ * Ventoy.h
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef __VENTOY_H__
+#define __VENTOY_H__
+
+#define COMPILE_ASSERT(expr)  extern char __compile_assert[(expr) ? 1 : -1]
+
+#define VENTOY_GUID { 0x77772020, 0x2e77, 0x6576, { 0x6e, 0x74, 0x6f, 0x79, 0x2e, 0x6e, 0x65, 0x74 }}
+
+#pragma pack(1)
+
+typedef struct ventoy_guid
+{
+    UINT32   data1;
+    UINT16   data2;
+    UINT16   data3;
+    UINT8    data4[8];
+}ventoy_guid;
+
+typedef struct ventoy_image_disk_region
+{
+    UINT32   image_sector_count; /* image sectors contained in this region */
+    UINT32   image_start_sector; /* image sector start */
+    UINT64   disk_start_sector;  /* disk sector start */
+}ventoy_image_disk_region;
+
+typedef struct ventoy_image_location
+{
+    ventoy_guid  guid;
+    
+    /* image sector size, currently this value is always 2048 */
+    UINT32   image_sector_size;
+
+    /* disk sector size, normally the value is 512 */
+    UINT32   disk_sector_size;
+
+    UINT32   region_count;
+    
+    /*
+     * disk region data
+     * If the image file has more than one fragments in disk, 
+     * there will be more than one region data here.
+     *
+     */
+    ventoy_image_disk_region regions[1];
+
+    /* ventoy_image_disk_region regions[2~region_count-1] */
+}ventoy_image_location;
+
+typedef struct ventoy_os_param
+{
+    ventoy_guid    guid;                  // VENTOY_GUID
+    UINT8   chksum;                // checksum
+
+    UINT8   vtoy_disk_guid[16];
+    UINT64  vtoy_disk_size;       // disk size in bytes
+    UINT16  vtoy_disk_part_id;    // begin with 1
+    UINT16  vtoy_disk_part_type;  // 0:exfat   1:ntfs  other: reserved
+    char    vtoy_img_path[384];   // It seems to be enough, utf-8 format
+    UINT64  vtoy_img_size;        // image file size in bytes
+
+    /* 
+     * Ventoy will write a copy of ventoy_image_location data into runtime memory
+     * this is the physically address and length of that memory.
+     * Address 0 means no such data exist.
+     * Address will be aligned by 4KB.
+     *
+     */
+    UINT64  vtoy_img_location_addr;
+    UINT32  vtoy_img_location_len;
+    
+    UINT64  vtoy_reserved[4];     // Internal use by ventoy
+
+    UINT8   reserved[31];
+}ventoy_os_param;
+
+#pragma pack()
+
+// compile assert to check that size of ventoy_os_param must be 512
+COMPILE_ASSERT(sizeof(ventoy_os_param) == 512);
+
+
+
+#pragma pack(4)
+
+typedef struct ventoy_chain_head
+{
+    ventoy_os_param os_param;
+
+    UINT32 disk_drive;
+    UINT32 drive_map;
+    UINT32 disk_sector_size;
+
+    UINT64 real_img_size_in_bytes;
+    UINT64 virt_img_size_in_bytes;
+    UINT32 boot_catalog;
+    UINT8  boot_catalog_sector[2048];
+    
+    UINT32 img_chunk_offset;
+    UINT32 img_chunk_num;
+
+    UINT32 override_chunk_offset;
+    UINT32 override_chunk_num;
+
+    UINT32 virt_chunk_offset;
+    UINT32 virt_chunk_num;
+}ventoy_chain_head;
+
+
+typedef struct ventoy_img_chunk
+{
+    UINT32 img_start_sector; //2KB
+    UINT32 img_end_sector;
+
+    UINT64 disk_start_sector; // in disk_sector_size
+    UINT64 disk_end_sector;
+}ventoy_img_chunk;
+
+
+typedef struct ventoy_override_chunk
+{
+    UINT64 img_offset;
+    UINT32 override_size;
+    UINT8  override_data[512];
+}ventoy_override_chunk;
+
+typedef struct ventoy_virt_chunk
+{
+    UINT32 mem_sector_start;
+    UINT32 mem_sector_end;
+    UINT32 mem_sector_offset;
+    UINT32 remap_sector_start;
+    UINT32 remap_sector_end;
+    UINT32 org_sector_start;
+}ventoy_virt_chunk;
+
+
+#pragma pack()
+
+
+#define VTOY_BLOCK_DEVICE_PATH_GUID                                    \
+       { 0x37b87ac6, 0xc180, 0x4583, { 0xa7, 0x05, 0x41, 0x4d, 0xa8, 0xf7, 0x7e, 0xd2 }}
+
+#define VTOY_BLOCK_DEVICE_PATH_NAME  L"ventoy"
+
+#if   defined (MDE_CPU_IA32)
+  #define VENTOY_UEFI_DESC   L"IA32 UEFI"
+#elif defined (MDE_CPU_X64)
+  #define VENTOY_UEFI_DESC   L"X64 UEFI"
+#elif defined (MDE_CPU_EBC)
+#elif defined (MDE_CPU_ARM)
+  #define VENTOY_UEFI_DESC   L"ARM UEFI"
+#elif defined (MDE_CPU_AARCH64)
+  #define VENTOY_UEFI_DESC   L"ARM64 UEFI"
+#else
+  #error Unknown Processor Type
+#endif
+
+typedef struct ventoy_sector_flag
+{
+    UINT8 flag; // 0:init   1:mem  2:remap
+    UINT64 remap_lba;    
+}ventoy_sector_flag;
+
+
+typedef struct vtoy_block_data 
+{
+       EFI_HANDLE Handle;
+       EFI_BLOCK_IO_MEDIA Media;       /* Media descriptor */
+       EFI_BLOCK_IO_PROTOCOL BlockIo;  /* Block I/O protocol */
+
+    UINTN DevicePathCompareLen;
+       EFI_DEVICE_PATH_PROTOCOL *Path; /* Device path protocol */
+
+    EFI_HANDLE RawBlockIoHandle;
+    EFI_BLOCK_IO_PROTOCOL *pRawBlockIo;
+    EFI_DEVICE_PATH_PROTOCOL *pDiskDevPath;
+
+    /* ventoy disk part2 ESP */
+    EFI_HANDLE DiskFsHandle;
+    EFI_SIMPLE_FILE_SYSTEM_PROTOCOL *pDiskFs;
+    EFI_DEVICE_PATH_PROTOCOL *pDiskFsDevPath;
+
+    EFI_HANDLE IsoDriverImage;
+}vtoy_block_data;
+
+#define ISO9660_EFI_DRIVER_PATH  L"\\ventoy\\iso9660_x64.efi"
+
+#define debug(expr, ...) if (gDebugPrint) VtoyDebug("[VTOY] "expr"\r\n", ##__VA_ARGS__)
+#define sleep(sec) gBS->Stall(1000000 * (sec))
+
+#define ventoy_debug_pause() \
+if (gDebugPrint) \
+{ \
+    UINTN __Index = 0; \
+    gST->ConOut->OutputString(gST->ConOut, L"[VTOY] ###### Press Enter to continue... ######\r\n");\
+    gST->ConIn->Reset(gST->ConIn, FALSE); \
+    gBS->WaitForEvent(1, &gST->ConIn->WaitForKey, &__Index);\
+}
+
+typedef const char * (*grub_env_get_pf)(const char *name);
+
+#pragma pack(1)
+typedef struct ventoy_grub_param
+{
+    grub_env_get_pf grub_env_get;
+}ventoy_grub_param;
+#pragma pack()
+
+
+extern BOOLEAN gDebugPrint;
+VOID EFIAPI VtoyDebug(IN CONST CHAR8  *Format, ...);
+
+#endif
+
diff --git a/EDK2/edk2-edk2-stable201911/MdeModulePkg/Application/Ventoy/Ventoy.inf b/EDK2/edk2-edk2-stable201911/MdeModulePkg/Application/Ventoy/Ventoy.inf
new file mode 100644 (file)
index 0000000..057577e
--- /dev/null
@@ -0,0 +1,49 @@
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+[Defines]
+  INF_VERSION                    = 0x00010005
+  BASE_NAME                      = Ventoy
+  FILE_GUID                      = 1c3a0915-09dc-49c2-873d-0aaaa7733299
+  MODULE_TYPE                    = UEFI_APPLICATION
+  VERSION_STRING                 = 1.0
+  ENTRY_POINT                    = VentoyEfiMain
+
+
+[Sources]
+  Ventoy.h
+  Ventoy.c
+
+[Packages]
+  MdePkg/MdePkg.dec
+  MdeModulePkg/MdeModulePkg.dec
+  ShellPkg/ShellPkg.dec
+
+[LibraryClasses]
+  UefiApplicationEntryPoint
+  UefiLib
+  DebugLib
+
+[Guids]
+  gShellVariableGuid
+  
+[Protocols]
+  gEfiLoadedImageProtocolGuid
+  gEfiBlockIoProtocolGuid
+  gEfiDevicePathProtocolGuid
+  gEfiSimpleFileSystemProtocolGuid
diff --git a/EDK2/edk2-edk2-stable201911/MdeModulePkg/MdeModulePkg.dsc b/EDK2/edk2-edk2-stable201911/MdeModulePkg/MdeModulePkg.dsc
new file mode 100644 (file)
index 0000000..2bdadd2
--- /dev/null
@@ -0,0 +1,496 @@
+## @file
+# EFI/PI Reference Module Package for All Architectures
+#
+# (C) Copyright 2014 Hewlett-Packard Development Company, L.P.<BR>
+# Copyright (c) 2007 - 2019, Intel Corporation. All rights reserved.<BR>
+#
+#    SPDX-License-Identifier: BSD-2-Clause-Patent
+#
+##
+
+[Defines]
+  PLATFORM_NAME                  = MdeModule
+  PLATFORM_GUID                  = 587CE499-6CBE-43cd-94E2-186218569478
+  PLATFORM_VERSION               = 0.98
+  DSC_SPECIFICATION              = 0x00010005
+  OUTPUT_DIRECTORY               = Build/MdeModule
+  SUPPORTED_ARCHITECTURES        = IA32|X64|EBC|ARM|AARCH64
+  BUILD_TARGETS                  = DEBUG|RELEASE|NOOPT
+  SKUID_IDENTIFIER               = DEFAULT
+
+[LibraryClasses]
+  #
+  # Entry point
+  #
+  PeiCoreEntryPoint|MdePkg/Library/PeiCoreEntryPoint/PeiCoreEntryPoint.inf
+  PeimEntryPoint|MdePkg/Library/PeimEntryPoint/PeimEntryPoint.inf
+  DxeCoreEntryPoint|MdePkg/Library/DxeCoreEntryPoint/DxeCoreEntryPoint.inf
+  UefiDriverEntryPoint|MdePkg/Library/UefiDriverEntryPoint/UefiDriverEntryPoint.inf
+  UefiApplicationEntryPoint|MdePkg/Library/UefiApplicationEntryPoint/UefiApplicationEntryPoint.inf
+  #
+  # Basic
+  #
+  BaseLib|MdePkg/Library/BaseLib/BaseLib.inf
+  BaseMemoryLib|MdePkg/Library/BaseMemoryLib/BaseMemoryLib.inf
+  SynchronizationLib|MdePkg/Library/BaseSynchronizationLib/BaseSynchronizationLib.inf
+  PrintLib|MdePkg/Library/BasePrintLib/BasePrintLib.inf
+  IoLib|MdePkg/Library/BaseIoLibIntrinsic/BaseIoLibIntrinsic.inf
+  PciLib|MdePkg/Library/BasePciLibCf8/BasePciLibCf8.inf
+  PciCf8Lib|MdePkg/Library/BasePciCf8Lib/BasePciCf8Lib.inf
+  PciSegmentLib|MdePkg/Library/BasePciSegmentLibPci/BasePciSegmentLibPci.inf
+  CacheMaintenanceLib|MdePkg/Library/BaseCacheMaintenanceLib/BaseCacheMaintenanceLib.inf
+  PeCoffLib|MdePkg/Library/BasePeCoffLib/BasePeCoffLib.inf
+  PeCoffGetEntryPointLib|MdePkg/Library/BasePeCoffGetEntryPointLib/BasePeCoffGetEntryPointLib.inf
+  SortLib|MdeModulePkg/Library/BaseSortLib/BaseSortLib.inf
+  #
+  # UEFI & PI
+  #
+  UefiBootServicesTableLib|MdePkg/Library/UefiBootServicesTableLib/UefiBootServicesTableLib.inf
+  UefiRuntimeServicesTableLib|MdePkg/Library/UefiRuntimeServicesTableLib/UefiRuntimeServicesTableLib.inf
+  UefiRuntimeLib|MdePkg/Library/UefiRuntimeLib/UefiRuntimeLib.inf
+  UefiLib|MdePkg/Library/UefiLib/UefiLib.inf
+  UefiHiiServicesLib|MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf
+  HiiLib|MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf
+  DevicePathLib|MdePkg/Library/UefiDevicePathLib/UefiDevicePathLib.inf
+  UefiDecompressLib|MdePkg/Library/BaseUefiDecompressLib/BaseUefiDecompressLib.inf
+  PeiServicesTablePointerLib|MdePkg/Library/PeiServicesTablePointerLib/PeiServicesTablePointerLib.inf
+  PeiServicesLib|MdePkg/Library/PeiServicesLib/PeiServicesLib.inf
+  DxeServicesLib|MdePkg/Library/DxeServicesLib/DxeServicesLib.inf
+  DxeServicesTableLib|MdePkg/Library/DxeServicesTableLib/DxeServicesTableLib.inf
+  UefiBootManagerLib|MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf
+  #
+  # Generic Modules
+  #
+  UefiUsbLib|MdePkg/Library/UefiUsbLib/UefiUsbLib.inf
+  UefiScsiLib|MdePkg/Library/UefiScsiLib/UefiScsiLib.inf
+  SecurityManagementLib|MdeModulePkg/Library/DxeSecurityManagementLib/DxeSecurityManagementLib.inf
+  TimerLib|MdePkg/Library/BaseTimerLibNullTemplate/BaseTimerLibNullTemplate.inf
+  SerialPortLib|MdePkg/Library/BaseSerialPortLibNull/BaseSerialPortLibNull.inf
+  CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.inf
+  PcdLib|MdePkg/Library/BasePcdLibNull/BasePcdLibNull.inf
+  CustomizedDisplayLib|MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf
+  FrameBufferBltLib|MdeModulePkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf
+  #
+  # Misc
+  #
+  DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
+  DebugPrintErrorLevelLib|MdePkg/Library/BaseDebugPrintErrorLevelLib/BaseDebugPrintErrorLevelLib.inf
+  ReportStatusCodeLib|MdePkg/Library/BaseReportStatusCodeLibNull/BaseReportStatusCodeLibNull.inf
+  PeCoffExtraActionLib|MdePkg/Library/BasePeCoffExtraActionLibNull/BasePeCoffExtraActionLibNull.inf
+  PerformanceLib|MdePkg/Library/BasePerformanceLibNull/BasePerformanceLibNull.inf
+  DebugAgentLib|MdeModulePkg/Library/DebugAgentLibNull/DebugAgentLibNull.inf
+  PlatformHookLib|MdeModulePkg/Library/BasePlatformHookLibNull/BasePlatformHookLibNull.inf
+  ResetSystemLib|MdeModulePkg/Library/BaseResetSystemLibNull/BaseResetSystemLibNull.inf
+  SmbusLib|MdePkg/Library/DxeSmbusLib/DxeSmbusLib.inf
+  S3BootScriptLib|MdeModulePkg/Library/PiDxeS3BootScriptLib/DxeS3BootScriptLib.inf
+  CpuExceptionHandlerLib|MdeModulePkg/Library/CpuExceptionHandlerLibNull/CpuExceptionHandlerLibNull.inf
+  PlatformBootManagerLib|MdeModulePkg/Library/PlatformBootManagerLibNull/PlatformBootManagerLibNull.inf
+  PciHostBridgeLib|MdeModulePkg/Library/PciHostBridgeLibNull/PciHostBridgeLibNull.inf
+  TpmMeasurementLib|MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf
+  AuthVariableLib|MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLibNull.inf
+  VarCheckLib|MdeModulePkg/Library/VarCheckLib/VarCheckLib.inf
+  FileExplorerLib|MdeModulePkg/Library/FileExplorerLib/FileExplorerLib.inf
+  NonDiscoverableDeviceRegistrationLib|MdeModulePkg/Library/NonDiscoverableDeviceRegistrationLib/NonDiscoverableDeviceRegistrationLib.inf
+
+  FmpAuthenticationLib|MdeModulePkg/Library/FmpAuthenticationLibNull/FmpAuthenticationLibNull.inf
+  CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.inf
+  BmpSupportLib|MdeModulePkg/Library/BaseBmpSupportLib/BaseBmpSupportLib.inf
+  SafeIntLib|MdePkg/Library/BaseSafeIntLib/BaseSafeIntLib.inf
+  DisplayUpdateProgressLib|MdeModulePkg/Library/DisplayUpdateProgressLibGraphics/DisplayUpdateProgressLibGraphics.inf
+
+[LibraryClasses.EBC.PEIM]
+  IoLib|MdePkg/Library/PeiIoLibCpuIo/PeiIoLibCpuIo.inf
+
+[LibraryClasses.common.PEI_CORE]
+  HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf
+  MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf
+
+[LibraryClasses.common.PEIM]
+  HobLib|MdePkg/Library/PeiHobLib/PeiHobLib.inf
+  MemoryAllocationLib|MdePkg/Library/PeiMemoryAllocationLib/PeiMemoryAllocationLib.inf
+  ExtractGuidedSectionLib|MdePkg/Library/PeiExtractGuidedSectionLib/PeiExtractGuidedSectionLib.inf
+  LockBoxLib|MdeModulePkg/Library/SmmLockBoxLib/SmmLockBoxPeiLib.inf
+
+[LibraryClasses.common.DXE_CORE]
+  HobLib|MdePkg/Library/DxeCoreHobLib/DxeCoreHobLib.inf
+  MemoryAllocationLib|MdeModulePkg/Library/DxeCoreMemoryAllocationLib/DxeCoreMemoryAllocationLib.inf
+  ExtractGuidedSectionLib|MdePkg/Library/DxeExtractGuidedSectionLib/DxeExtractGuidedSectionLib.inf
+
+[LibraryClasses.common.DXE_DRIVER]
+  HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf
+  LockBoxLib|MdeModulePkg/Library/SmmLockBoxLib/SmmLockBoxDxeLib.inf
+  MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+  ExtractGuidedSectionLib|MdePkg/Library/DxeExtractGuidedSectionLib/DxeExtractGuidedSectionLib.inf
+  CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf
+
+[LibraryClasses.common.DXE_RUNTIME_DRIVER]
+  HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf
+  MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+  DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf
+  LockBoxLib|MdeModulePkg/Library/SmmLockBoxLib/SmmLockBoxDxeLib.inf
+  CapsuleLib|MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf
+
+[LibraryClasses.common.SMM_CORE]
+  HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf
+  MemoryAllocationLib|MdeModulePkg/Library/PiSmmCoreMemoryAllocationLib/PiSmmCoreMemoryAllocationLib.inf
+  SmmServicesTableLib|MdeModulePkg/Library/PiSmmCoreSmmServicesTableLib/PiSmmCoreSmmServicesTableLib.inf
+  SmmCorePlatformHookLib|MdeModulePkg/Library/SmmCorePlatformHookLibNull/SmmCorePlatformHookLibNull.inf
+  SmmMemLib|MdePkg/Library/SmmMemLib/SmmMemLib.inf
+
+[LibraryClasses.common.DXE_SMM_DRIVER]
+  HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf
+  DebugLib|MdePkg/Library/BaseDebugLibNull/BaseDebugLibNull.inf
+  MemoryAllocationLib|MdePkg/Library/SmmMemoryAllocationLib/SmmMemoryAllocationLib.inf
+  MmServicesTableLib|MdePkg/Library/MmServicesTableLib/MmServicesTableLib.inf
+  SmmServicesTableLib|MdePkg/Library/SmmServicesTableLib/SmmServicesTableLib.inf
+  LockBoxLib|MdeModulePkg/Library/SmmLockBoxLib/SmmLockBoxSmmLib.inf
+  SmmMemLib|MdePkg/Library/SmmMemLib/SmmMemLib.inf
+
+[LibraryClasses.common.UEFI_DRIVER]
+  HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf
+  MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+  DebugLib|MdePkg/Library/UefiDebugLibConOut/UefiDebugLibConOut.inf
+  LockBoxLib|MdeModulePkg/Library/SmmLockBoxLib/SmmLockBoxDxeLib.inf
+
+[LibraryClasses.common.UEFI_APPLICATION]
+  HobLib|MdePkg/Library/DxeHobLib/DxeHobLib.inf
+  MemoryAllocationLib|MdePkg/Library/UefiMemoryAllocationLib/UefiMemoryAllocationLib.inf
+  DebugLib|MdePkg/Library/UefiDebugLibStdErr/UefiDebugLibStdErr.inf
+  FileHandleLib|MdePkg/Library/UefiFileHandleLib/UefiFileHandleLib.inf
+
+[LibraryClasses.common.MM_STANDALONE]
+  HobLib|MdeModulePkg/Library/BaseHobLibNull/BaseHobLibNull.inf
+  MemoryAllocationLib|MdeModulePkg/Library/BaseMemoryAllocationLibNull/BaseMemoryAllocationLibNull.inf
+  StandaloneMmDriverEntryPoint|MdePkg/Library/StandaloneMmDriverEntryPoint/StandaloneMmDriverEntryPoint.inf
+  MmServicesTableLib|MdePkg/Library/StandaloneMmServicesTableLib/StandaloneMmServicesTableLib.inf
+
+[LibraryClasses.ARM, LibraryClasses.AARCH64]
+  ArmLib|ArmPkg/Library/ArmLib/ArmBaseLib.inf
+  ArmMmuLib|ArmPkg/Library/ArmMmuLib/ArmMmuBaseLib.inf
+  LockBoxLib|MdeModulePkg/Library/LockBoxNullLib/LockBoxNullLib.inf
+
+  #
+  # It is not possible to prevent ARM compiler calls to generic intrinsic functions.
+  # This library provides the instrinsic functions generated by a given compiler.
+  # [LibraryClasses.ARM] and NULL mean link this library into all ARM images.
+  #
+  NULL|ArmPkg/Library/CompilerIntrinsicsLib/CompilerIntrinsicsLib.inf
+
+  #
+  # Since software stack checking may be heuristically enabled by the compiler
+  # include BaseStackCheckLib unconditionally.
+  #
+  NULL|MdePkg/Library/BaseStackCheckLib/BaseStackCheckLib.inf
+
+[LibraryClasses.EBC]
+  LockBoxLib|MdeModulePkg/Library/LockBoxNullLib/LockBoxNullLib.inf
+
+[PcdsFeatureFlag]
+  gEfiMdePkgTokenSpaceGuid.PcdDriverDiagnostics2Disable|TRUE
+  gEfiMdePkgTokenSpaceGuid.PcdComponentName2Disable|TRUE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdInstallAcpiSdtProtocol|TRUE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdDevicePathSupportDevicePathFromText|FALSE
+  gEfiMdeModulePkgTokenSpaceGuid.PcdDevicePathSupportDevicePathToText|FALSE
+
+[PcdsFixedAtBuild]
+  gEfiMdePkgTokenSpaceGuid.PcdDebugPropertyMask|0x0f
+  gEfiMdePkgTokenSpaceGuid.PcdReportStatusCodePropertyMask|0x06
+  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxSizeNonPopulateCapsule|0x0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxSizePopulateCapsule|0x0
+  gEfiMdeModulePkgTokenSpaceGuid.PcdMaxPeiPerformanceLogEntries|28
+
+[PcdsDynamicExDefault]
+  gEfiMdeModulePkgTokenSpaceGuid.PcdRecoveryFileName|L"FVMAIN.FV"
+
+[Components]
+  MdeModulePkg/Application/Ventoy/Ventoy.inf
+  MdeModulePkg/Application/HelloWorld/HelloWorld.inf
+  MdeModulePkg/Application/DumpDynPcd/DumpDynPcd.inf
+  MdeModulePkg/Application/MemoryProfileInfo/MemoryProfileInfo.inf
+
+  MdeModulePkg/Library/UefiSortLib/UefiSortLib.inf
+  MdeModulePkg/Logo/Logo.inf
+  MdeModulePkg/Logo/LogoDxe.inf
+  MdeModulePkg/Library/BaseSortLib/BaseSortLib.inf
+  MdeModulePkg/Library/BootMaintenanceManagerUiLib/BootMaintenanceManagerUiLib.inf
+  MdeModulePkg/Library/BootManagerUiLib/BootManagerUiLib.inf
+  MdeModulePkg/Library/CustomizedDisplayLib/CustomizedDisplayLib.inf
+  MdeModulePkg/Library/DebugAgentLibNull/DebugAgentLibNull.inf
+  MdeModulePkg/Library/DeviceManagerUiLib/DeviceManagerUiLib.inf
+  MdeModulePkg/Library/LockBoxNullLib/LockBoxNullLib.inf
+  MdeModulePkg/Library/PciHostBridgeLibNull/PciHostBridgeLibNull.inf
+  MdeModulePkg/Library/PiSmmCoreSmmServicesTableLib/PiSmmCoreSmmServicesTableLib.inf
+  MdeModulePkg/Library/UefiHiiServicesLib/UefiHiiServicesLib.inf
+  MdeModulePkg/Library/BaseHobLibNull/BaseHobLibNull.inf
+  MdeModulePkg/Library/BaseMemoryAllocationLibNull/BaseMemoryAllocationLibNull.inf
+
+  MdeModulePkg/Bus/Pci/PciHostBridgeDxe/PciHostBridgeDxe.inf
+  MdeModulePkg/Bus/Pci/PciSioSerialDxe/PciSioSerialDxe.inf
+  MdeModulePkg/Bus/Pci/PciBusDxe/PciBusDxe.inf
+  MdeModulePkg/Bus/Pci/IncompatiblePciDeviceSupportDxe/IncompatiblePciDeviceSupportDxe.inf
+  MdeModulePkg/Bus/Pci/NvmExpressDxe/NvmExpressDxe.inf
+  MdeModulePkg/Bus/Pci/NvmExpressPei/NvmExpressPei.inf
+  MdeModulePkg/Bus/Pci/SdMmcPciHcDxe/SdMmcPciHcDxe.inf
+  MdeModulePkg/Bus/Pci/SdMmcPciHcPei/SdMmcPciHcPei.inf
+  MdeModulePkg/Bus/Sd/EmmcBlockIoPei/EmmcBlockIoPei.inf
+  MdeModulePkg/Bus/Sd/SdBlockIoPei/SdBlockIoPei.inf
+  MdeModulePkg/Bus/Sd/EmmcDxe/EmmcDxe.inf
+  MdeModulePkg/Bus/Sd/SdDxe/SdDxe.inf
+  MdeModulePkg/Bus/Pci/UfsPciHcDxe/UfsPciHcDxe.inf
+  MdeModulePkg/Bus/Ufs/UfsPassThruDxe/UfsPassThruDxe.inf
+  MdeModulePkg/Bus/Pci/UfsPciHcPei/UfsPciHcPei.inf
+  MdeModulePkg/Bus/Ufs/UfsBlockIoPei/UfsBlockIoPei.inf
+  MdeModulePkg/Bus/Pci/XhciDxe/XhciDxe.inf
+  MdeModulePkg/Bus/Pci/EhciDxe/EhciDxe.inf
+  MdeModulePkg/Bus/Pci/UhciDxe/UhciDxe.inf
+  MdeModulePkg/Bus/Pci/UhciPei/UhciPei.inf
+  MdeModulePkg/Bus/Pci/EhciPei/EhciPei.inf
+  MdeModulePkg/Bus/Pci/XhciPei/XhciPei.inf
+  MdeModulePkg/Bus/Pci/IdeBusPei/IdeBusPei.inf
+  MdeModulePkg/Bus/Usb/UsbBusPei/UsbBusPei.inf
+  MdeModulePkg/Bus/Usb/UsbBotPei/UsbBotPei.inf
+  MdeModulePkg/Bus/Pci/SataControllerDxe/SataControllerDxe.inf
+  MdeModulePkg/Bus/Ata/AtaBusDxe/AtaBusDxe.inf
+  MdeModulePkg/Bus/Ata/AtaAtapiPassThru/AtaAtapiPassThru.inf
+  MdeModulePkg/Bus/Ata/AhciPei/AhciPei.inf
+  MdeModulePkg/Bus/Scsi/ScsiBusDxe/ScsiBusDxe.inf
+  MdeModulePkg/Bus/Scsi/ScsiDiskDxe/ScsiDiskDxe.inf
+  MdeModulePkg/Bus/Usb/UsbBusDxe/UsbBusDxe.inf
+  MdeModulePkg/Bus/Usb/UsbKbDxe/UsbKbDxe.inf
+  MdeModulePkg/Bus/Usb/UsbMassStorageDxe/UsbMassStorageDxe.inf
+  MdeModulePkg/Bus/Usb/UsbMouseAbsolutePointerDxe/UsbMouseAbsolutePointerDxe.inf
+  MdeModulePkg/Bus/Usb/UsbMouseDxe/UsbMouseDxe.inf
+  MdeModulePkg/Bus/I2c/I2cDxe/I2cBusDxe.inf
+  MdeModulePkg/Bus/I2c/I2cDxe/I2cHostDxe.inf
+  MdeModulePkg/Bus/I2c/I2cDxe/I2cDxe.inf
+  MdeModulePkg/Bus/Isa/IsaBusDxe/IsaBusDxe.inf
+  MdeModulePkg/Bus/Isa/Ps2KeyboardDxe/Ps2KeyboardDxe.inf
+  MdeModulePkg/Bus/Isa/Ps2MouseDxe/Ps2MouseDxe.inf
+  MdeModulePkg/Bus/Pci/NonDiscoverablePciDeviceDxe/NonDiscoverablePciDeviceDxe.inf
+
+  MdeModulePkg/Core/DxeIplPeim/DxeIpl.inf
+  MdeModulePkg/Core/Pei/PeiMain.inf
+  MdeModulePkg/Core/RuntimeDxe/RuntimeDxe.inf
+
+  MdeModulePkg/Library/DxeCapsuleLibNull/DxeCapsuleLibNull.inf
+  MdeModulePkg/Library/UefiMemoryAllocationProfileLib/UefiMemoryAllocationProfileLib.inf
+  MdeModulePkg/Library/DxeCoreMemoryAllocationLib/DxeCoreMemoryAllocationLib.inf
+  MdeModulePkg/Library/DxeCoreMemoryAllocationLib/DxeCoreMemoryAllocationProfileLib.inf
+  MdeModulePkg/Library/DxeCorePerformanceLib/DxeCorePerformanceLib.inf
+  MdeModulePkg/Library/DxeCrc32GuidedSectionExtractLib/DxeCrc32GuidedSectionExtractLib.inf
+  MdeModulePkg/Library/DxePerformanceLib/DxePerformanceLib.inf
+  MdeModulePkg/Library/DxeResetSystemLib/DxeResetSystemLib.inf
+  MdeModulePkg/Library/DxePrintLibPrint2Protocol/DxePrintLibPrint2Protocol.inf
+  MdeModulePkg/Library/PeiCrc32GuidedSectionExtractLib/PeiCrc32GuidedSectionExtractLib.inf
+  MdeModulePkg/Library/PeiPerformanceLib/PeiPerformanceLib.inf
+  MdeModulePkg/Library/PeiResetSystemLib/PeiResetSystemLib.inf
+  MdeModulePkg/Library/UefiHiiLib/UefiHiiLib.inf
+  MdeModulePkg/Library/ResetUtilityLib/ResetUtilityLib.inf
+  MdeModulePkg/Library/BaseResetSystemLibNull/BaseResetSystemLibNull.inf
+  MdeModulePkg/Library/DxeSecurityManagementLib/DxeSecurityManagementLib.inf
+  MdeModulePkg/Library/OemHookStatusCodeLibNull/OemHookStatusCodeLibNull.inf
+  MdeModulePkg/Library/PeiReportStatusCodeLib/PeiReportStatusCodeLib.inf
+  MdeModulePkg/Library/DxeReportStatusCodeLib/DxeReportStatusCodeLib.inf
+  MdeModulePkg/Library/RuntimeDxeReportStatusCodeLib/RuntimeDxeReportStatusCodeLib.inf
+  MdeModulePkg/Library/RuntimeResetSystemLib/RuntimeResetSystemLib.inf
+  MdeModulePkg/Library/BaseSerialPortLib16550/BaseSerialPortLib16550.inf
+  MdeModulePkg/Library/BasePlatformHookLibNull/BasePlatformHookLibNull.inf
+  MdeModulePkg/Library/DxeDebugPrintErrorLevelLib/DxeDebugPrintErrorLevelLib.inf
+  MdeModulePkg/Library/PiDxeS3BootScriptLib/DxeS3BootScriptLib.inf
+  MdeModulePkg/Library/PeiDebugPrintHobLib/PeiDebugPrintHobLib.inf
+  MdeModulePkg/Library/CpuExceptionHandlerLibNull/CpuExceptionHandlerLibNull.inf
+  MdeModulePkg/Library/PlatformHookLibSerialPortPpi/PlatformHookLibSerialPortPpi.inf
+  MdeModulePkg/Library/LzmaCustomDecompressLib/LzmaCustomDecompressLib.inf
+  MdeModulePkg/Library/PeiDxeDebugLibReportStatusCode/PeiDxeDebugLibReportStatusCode.inf
+  MdeModulePkg/Library/PeiDebugLibDebugPpi/PeiDebugLibDebugPpi.inf
+  MdeModulePkg/Library/UefiBootManagerLib/UefiBootManagerLib.inf
+  MdeModulePkg/Library/PlatformBootManagerLibNull/PlatformBootManagerLibNull.inf
+  MdeModulePkg/Library/BootLogoLib/BootLogoLib.inf
+  MdeModulePkg/Library/TpmMeasurementLibNull/TpmMeasurementLibNull.inf
+  MdeModulePkg/Library/AuthVariableLibNull/AuthVariableLibNull.inf
+  MdeModulePkg/Library/VarCheckLib/VarCheckLib.inf
+  MdeModulePkg/Library/VarCheckHiiLib/VarCheckHiiLib.inf
+  MdeModulePkg/Library/VarCheckPcdLib/VarCheckPcdLib.inf
+  MdeModulePkg/Library/PlatformVarCleanupLib/PlatformVarCleanupLib.inf
+  MdeModulePkg/Library/FileExplorerLib/FileExplorerLib.inf
+  MdeModulePkg/Library/DxeFileExplorerProtocol/DxeFileExplorerProtocol.inf
+  MdeModulePkg/Library/BaseIpmiLibNull/BaseIpmiLibNull.inf
+  MdeModulePkg/Library/DxeIpmiLibIpmiProtocol/DxeIpmiLibIpmiProtocol.inf
+  MdeModulePkg/Library/PeiIpmiLibIpmiPpi/PeiIpmiLibIpmiPpi.inf
+  MdeModulePkg/Library/SmmIpmiLibSmmIpmiProtocol/SmmIpmiLibSmmIpmiProtocol.inf
+  MdeModulePkg/Library/FrameBufferBltLib/FrameBufferBltLib.inf
+  MdeModulePkg/Library/NonDiscoverableDeviceRegistrationLib/NonDiscoverableDeviceRegistrationLib.inf
+  MdeModulePkg/Library/BaseBmpSupportLib/BaseBmpSupportLib.inf
+  MdeModulePkg/Library/DisplayUpdateProgressLibGraphics/DisplayUpdateProgressLibGraphics.inf
+  MdeModulePkg/Library/DisplayUpdateProgressLibText/DisplayUpdateProgressLibText.inf
+
+  MdeModulePkg/Universal/BdsDxe/BdsDxe.inf
+  MdeModulePkg/Application/BootManagerMenuApp/BootManagerMenuApp.inf
+  MdeModulePkg/Application/UiApp/UiApp.inf{
+    <LibraryClasses>
+      NULL|MdeModulePkg/Library/DeviceManagerUiLib/DeviceManagerUiLib.inf
+      NULL|MdeModulePkg/Library/BootManagerUiLib/BootManagerUiLib.inf
+      NULL|MdeModulePkg/Library/BootMaintenanceManagerUiLib/BootMaintenanceManagerUiLib.inf
+  }
+  MdeModulePkg/Universal/DriverHealthManagerDxe/DriverHealthManagerDxe.inf
+  MdeModulePkg/Universal/BootManagerPolicyDxe/BootManagerPolicyDxe.inf
+  MdeModulePkg/Universal/CapsulePei/CapsulePei.inf
+  MdeModulePkg/Universal/CapsuleOnDiskLoadPei/CapsuleOnDiskLoadPei.inf
+  MdeModulePkg/Universal/CapsuleRuntimeDxe/CapsuleRuntimeDxe.inf
+  MdeModulePkg/Universal/Console/ConPlatformDxe/ConPlatformDxe.inf
+  MdeModulePkg/Universal/Console/ConSplitterDxe/ConSplitterDxe.inf
+  MdeModulePkg/Universal/Console/GraphicsConsoleDxe/GraphicsConsoleDxe.inf
+  MdeModulePkg/Universal/Console/GraphicsOutputDxe/GraphicsOutputDxe.inf
+  MdeModulePkg/Universal/Console/TerminalDxe/TerminalDxe.inf
+  MdeModulePkg/Universal/DebugPortDxe/DebugPortDxe.inf
+  MdeModulePkg/Universal/DevicePathDxe/DevicePathDxe.inf
+  MdeModulePkg/Universal/PrintDxe/PrintDxe.inf
+  MdeModulePkg/Universal/Disk/DiskIoDxe/DiskIoDxe.inf
+  MdeModulePkg/Universal/Disk/PartitionDxe/PartitionDxe.inf
+  MdeModulePkg/Universal/Disk/UdfDxe/UdfDxe.inf
+  MdeModulePkg/Universal/Disk/UnicodeCollation/EnglishDxe/EnglishDxe.inf
+  MdeModulePkg/Universal/Disk/CdExpressPei/CdExpressPei.inf
+  MdeModulePkg/Universal/DriverSampleDxe/DriverSampleDxe.inf
+  MdeModulePkg/Universal/HiiDatabaseDxe/HiiDatabaseDxe.inf
+  MdeModulePkg/Universal/MemoryTest/GenericMemoryTestDxe/GenericMemoryTestDxe.inf
+  MdeModulePkg/Universal/MemoryTest/NullMemoryTestDxe/NullMemoryTestDxe.inf
+  MdeModulePkg/Universal/Metronome/Metronome.inf
+  MdeModulePkg/Universal/MonotonicCounterRuntimeDxe/MonotonicCounterRuntimeDxe.inf
+  MdeModulePkg/Universal/ResetSystemPei/ResetSystemPei.inf {
+    <LibraryClasses>
+      ResetSystemLib|MdeModulePkg/Library/BaseResetSystemLibNull/BaseResetSystemLibNull.inf
+  }
+  MdeModulePkg/Universal/ResetSystemRuntimeDxe/ResetSystemRuntimeDxe.inf {
+    <LibraryClasses>
+      ResetSystemLib|MdeModulePkg/Library/BaseResetSystemLibNull/BaseResetSystemLibNull.inf
+  }
+  MdeModulePkg/Universal/SmbiosDxe/SmbiosDxe.inf
+  MdeModulePkg/Universal/SmbiosMeasurementDxe/SmbiosMeasurementDxe.inf
+
+  MdeModulePkg/Universal/PcatSingleSegmentPciCfg2Pei/PcatSingleSegmentPciCfg2Pei.inf
+  MdeModulePkg/Universal/PCD/Dxe/Pcd.inf
+  MdeModulePkg/Universal/PCD/Pei/Pcd.inf
+  MdeModulePkg/Universal/PlatformDriOverrideDxe/PlatformDriOverrideDxe.inf
+
+  MdeModulePkg/Universal/ReportStatusCodeRouter/Pei/ReportStatusCodeRouterPei.inf
+  MdeModulePkg/Universal/ReportStatusCodeRouter/RuntimeDxe/ReportStatusCodeRouterRuntimeDxe.inf
+
+  MdeModulePkg/Universal/SecurityStubDxe/SecurityStubDxe.inf
+  MdeModulePkg/Universal/SetupBrowserDxe/SetupBrowserDxe.inf
+  MdeModulePkg/Universal/DisplayEngineDxe/DisplayEngineDxe.inf
+  MdeModulePkg/Application/VariableInfo/VariableInfo.inf
+  MdeModulePkg/Universal/FaultTolerantWritePei/FaultTolerantWritePei.inf
+  MdeModulePkg/Universal/Variable/Pei/VariablePei.inf
+  MdeModulePkg/Universal/WatchdogTimerDxe/WatchdogTimer.inf
+  MdeModulePkg/Universal/TimestampDxe/TimestampDxe.inf
+  MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteDxe.inf
+
+  MdeModulePkg/Universal/Acpi/AcpiPlatformDxe/AcpiPlatformDxe.inf
+  MdeModulePkg/Universal/Acpi/AcpiTableDxe/AcpiTableDxe.inf
+  MdeModulePkg/Universal/HiiResourcesSampleDxe/HiiResourcesSampleDxe.inf
+  MdeModulePkg/Universal/LegacyRegion2Dxe/LegacyRegion2Dxe.inf
+
+  MdeModulePkg/Universal/StatusCodeHandler/Pei/StatusCodeHandlerPei.inf
+  MdeModulePkg/Universal/StatusCodeHandler/RuntimeDxe/StatusCodeHandlerRuntimeDxe.inf
+
+  MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTablePei/FirmwarePerformancePei.inf {
+    <LibraryClasses>
+      LockBoxLib|MdeModulePkg/Library/LockBoxNullLib/LockBoxNullLib.inf
+  }
+  MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableDxe/FirmwarePerformanceDxe.inf
+  MdeModulePkg/Universal/Acpi/BootGraphicsResourceTableDxe/BootGraphicsResourceTableDxe.inf
+  MdeModulePkg/Universal/SectionExtractionDxe/SectionExtractionDxe.inf {
+    <LibraryClasses>
+      NULL|MdeModulePkg/Library/DxeCrc32GuidedSectionExtractLib/DxeCrc32GuidedSectionExtractLib.inf
+  }
+  MdeModulePkg/Universal/SectionExtractionPei/SectionExtractionPei.inf {
+    <LibraryClasses>
+      NULL|MdeModulePkg/Library/PeiCrc32GuidedSectionExtractLib/PeiCrc32GuidedSectionExtractLib.inf
+  }
+
+  MdeModulePkg/Universal/FvSimpleFileSystemDxe/FvSimpleFileSystemDxe.inf
+  MdeModulePkg/Universal/EsrtDxe/EsrtDxe.inf
+  MdeModulePkg/Universal/EsrtFmpDxe/EsrtFmpDxe.inf
+
+  MdeModulePkg/Universal/FileExplorerDxe/FileExplorerDxe.inf  {
+    <LibraryClasses>
+      FileExplorerLib|MdeModulePkg/Library/FileExplorerLib/FileExplorerLib.inf
+  }
+
+  MdeModulePkg/Universal/SerialDxe/SerialDxe.inf
+  MdeModulePkg/Universal/LoadFileOnFv2/LoadFileOnFv2.inf
+
+  MdeModulePkg/Universal/DebugServicePei/DebugServicePei.inf
+
+  MdeModulePkg/Application/CapsuleApp/CapsuleApp.inf
+  MdeModulePkg/Library/FmpAuthenticationLibNull/FmpAuthenticationLibNull.inf
+  MdeModulePkg/Library/DxeCapsuleLibFmp/DxeCapsuleLib.inf
+  MdeModulePkg/Library/DxeCapsuleLibFmp/DxeRuntimeCapsuleLib.inf
+
+[Components.IA32, Components.X64, Components.AARCH64]
+  MdeModulePkg/Universal/EbcDxe/EbcDxe.inf
+  MdeModulePkg/Universal/EbcDxe/EbcDebugger.inf
+  MdeModulePkg/Universal/EbcDxe/EbcDebuggerConfig.inf
+
+[Components.IA32, Components.X64, Components.ARM, Components.AARCH64]
+  MdeModulePkg/Library/BrotliCustomDecompressLib/BrotliCustomDecompressLib.inf
+  MdeModulePkg/Library/VarCheckUefiLib/VarCheckUefiLib.inf
+  MdeModulePkg/Core/Dxe/DxeMain.inf {
+    <LibraryClasses>
+      NULL|MdeModulePkg/Library/DxeCrc32GuidedSectionExtractLib/DxeCrc32GuidedSectionExtractLib.inf
+  }
+
+!if $(TOOL_CHAIN_TAG) != "XCODE5"
+  MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteStandaloneMm.inf
+  MdeModulePkg/Universal/Variable/RuntimeDxe/VariableStandaloneMm.inf
+!endif
+
+[Components.IA32, Components.X64]
+  MdeModulePkg/Universal/DebugSupportDxe/DebugSupportDxe.inf
+  MdeModulePkg/Application/SmiHandlerProfileInfo/SmiHandlerProfileInfo.inf
+  MdeModulePkg/Core/PiSmmCore/PiSmmIpl.inf
+  MdeModulePkg/Core/PiSmmCore/PiSmmCore.inf
+  MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmm.inf {
+    <LibraryClasses>
+      NULL|MdeModulePkg/Library/VarCheckUefiLib/VarCheckUefiLib.inf
+      NULL|MdeModulePkg/Library/VarCheckHiiLib/VarCheckHiiLib.inf
+      NULL|MdeModulePkg/Library/VarCheckPcdLib/VarCheckPcdLib.inf
+  }
+  MdeModulePkg/Universal/Variable/RuntimeDxe/VariableRuntimeDxe.inf {
+    <LibraryClasses>
+      NULL|MdeModulePkg/Library/VarCheckUefiLib/VarCheckUefiLib.inf
+      NULL|MdeModulePkg/Library/VarCheckHiiLib/VarCheckHiiLib.inf
+      NULL|MdeModulePkg/Library/VarCheckPcdLib/VarCheckPcdLib.inf
+  }
+  MdeModulePkg/Universal/Variable/RuntimeDxe/VariableSmmRuntimeDxe.inf
+  MdeModulePkg/Library/SmmReportStatusCodeLib/SmmReportStatusCodeLib.inf
+  MdeModulePkg/Universal/StatusCodeHandler/Smm/StatusCodeHandlerSmm.inf
+  MdeModulePkg/Universal/ReportStatusCodeRouter/Smm/ReportStatusCodeRouterSmm.inf
+  MdeModulePkg/Universal/LockBox/SmmLockBox/SmmLockBox.inf
+  MdeModulePkg/Library/SmmMemoryAllocationProfileLib/SmmMemoryAllocationProfileLib.inf
+  MdeModulePkg/Library/PiSmmCoreMemoryAllocationLib/PiSmmCoreMemoryAllocationProfileLib.inf
+  MdeModulePkg/Library/PiSmmCoreMemoryAllocationLib/PiSmmCoreMemoryAllocationLib.inf
+  MdeModulePkg/Library/SmmCorePerformanceLib/SmmCorePerformanceLib.inf
+  MdeModulePkg/Library/SmmPerformanceLib/SmmPerformanceLib.inf
+  MdeModulePkg/Library/SmmLockBoxLib/SmmLockBoxPeiLib.inf
+  MdeModulePkg/Library/SmmLockBoxLib/SmmLockBoxDxeLib.inf
+  MdeModulePkg/Library/SmmLockBoxLib/SmmLockBoxSmmLib.inf
+  MdeModulePkg/Library/SmmCorePlatformHookLibNull/SmmCorePlatformHookLibNull.inf
+  MdeModulePkg/Library/SmmSmiHandlerProfileLib/SmmSmiHandlerProfileLib.inf
+  MdeModulePkg/Library/LzmaCustomDecompressLib/LzmaArchCustomDecompressLib.inf
+  MdeModulePkg/Universal/Acpi/BootScriptExecutorDxe/BootScriptExecutorDxe.inf
+  MdeModulePkg/Universal/Acpi/S3SaveStateDxe/S3SaveStateDxe.inf
+  MdeModulePkg/Universal/Acpi/SmmS3SaveState/SmmS3SaveState.inf
+  MdeModulePkg/Universal/Acpi/FirmwarePerformanceDataTableSmm/FirmwarePerformanceSmm.inf
+  MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmm.inf
+  MdeModulePkg/Universal/FaultTolerantWriteDxe/FaultTolerantWriteSmmDxe.inf
+  MdeModulePkg/Universal/RegularExpressionDxe/RegularExpressionDxe.inf
+  MdeModulePkg/Universal/SmmCommunicationBufferDxe/SmmCommunicationBufferDxe.inf
+  MdeModulePkg/Universal/Disk/RamDiskDxe/RamDiskDxe.inf
+
+[Components.X64]
+  MdeModulePkg/Universal/CapsulePei/CapsuleX64.inf
+
+[BuildOptions]
+  *_*_*_CC_FLAGS = -D DISABLE_NEW_DEPRECATED_INTERFACES
+
diff --git a/ExFAT/README.txt b/ExFAT/README.txt
new file mode 100644 (file)
index 0000000..5a7d039
--- /dev/null
@@ -0,0 +1,8 @@
+Please download exfat-1.3.0.zip and mirrors-libfuse-fuse-2.9.9.zip first.
+
+exfat-1.3.0.zip:
+https://codeload.github.com/relan/exfat/zip/v1.3.0
+
+mirrors-libfuse-fuse-2.9.9.zip:
+https://gitee.com/mirrors/libfuse/repository/archive/fuse-2.9.9.zip
+
diff --git a/ExFAT/buidexfat.sh b/ExFAT/buidexfat.sh
new file mode 100644 (file)
index 0000000..97d6a15
--- /dev/null
@@ -0,0 +1,62 @@
+#!/bin/bash
+# 
+# For 32bit, for example CentOS 6.10 i386 
+# automake 1.11.1 must update to automake 1.11.2
+# pkg-config must be installed
+#
+#
+
+if uname -a | egrep -q 'x86_64|amd64'; then
+    opt=
+else
+    opt=-lrt
+fi
+
+CUR="$PWD"
+
+if ! [ -e LIBFUSE ]; then
+       ./buidlibfuse.sh
+fi
+
+
+rm -f EXFAT/shared/*
+rm -f EXFAT/static/*
+
+
+rm -rf exfat-1.3.0
+unzip exfat-1.3.0.zip
+
+
+cd exfat-1.3.0
+autoreconf --install
+./configure --prefix="$CUR" CFLAGS='-static -O2 -D_FILE_OFFSET_BITS=64' FUSE_CFLAGS="-I$CUR/LIBFUSE/include/" FUSE_LIBS="$CUR/LIBFUSE/lib/libfuse.a -pthread $opt -ldl"
+make
+
+strip --strip-all fuse/mount.exfat-fuse
+strip --strip-all mkfs/mkexfatfs
+
+cp fuse/mount.exfat-fuse ../EXFAT/static/mount.exfat-fuse
+cp mkfs/mkexfatfs ../EXFAT/static/mkexfatfs
+
+cd ..
+rm -rf exfat-1.3.0
+
+unzip exfat-1.3.0.zip
+
+cd exfat-1.3.0
+autoreconf --install
+./configure --prefix="$CUR" CFLAGS='-O2 -D_FILE_OFFSET_BITS=64' FUSE_CFLAGS="-I$CUR/LIBFUSE/include/" FUSE_LIBS="$CUR/LIBFUSE/lib/libfuse.a -lpthread -ldl $opt"
+make
+
+strip --strip-all fuse/mount.exfat-fuse
+strip --strip-all mkfs/mkexfatfs
+
+cp fuse/mount.exfat-fuse ../EXFAT/shared/mount.exfat-fuse
+cp mkfs/mkexfatfs ../EXFAT/shared/mkexfatfs
+
+cd ..
+rm -rf exfat-1.3.0
+
+
+
+
diff --git a/ExFAT/buidlibfuse.sh b/ExFAT/buidlibfuse.sh
new file mode 100644 (file)
index 0000000..7e0ad01
--- /dev/null
@@ -0,0 +1,18 @@
+#!/bin/bash
+
+CUR="$PWD"
+
+rm -rf libfuse
+rm -rf LIBFUSE
+
+unzip mirrors-libfuse-fuse-2.9.9.zip
+
+
+cd libfuse
+./makeconf.sh
+
+./configure --prefix="$CUR/LIBFUSE"
+make -j 16
+make install
+cd ..
+rm -rf libfuse
diff --git a/FUSEISO/build.sh b/FUSEISO/build.sh
new file mode 100644 (file)
index 0000000..69f3b80
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/bash
+
+CUR="$PWD"
+
+#LIBFUSE_DIR=$CUR/LIBFUSE
+LIBFUSE_DIR=../ExFAT/LIBFUSE
+
+if uname -a | egrep -q 'x86_64|amd64'; then
+    name=vtoy_fuse_iso_64
+else
+    name=vtoy_fuse_iso_32
+    opt=-lrt
+fi
+
+export C_INCLUDE_PATH=$LIBFUSE_DIR/include
+
+rm -f $name
+gcc  -O2 -D_FILE_OFFSET_BITS=64  vtoy_fuse_iso.c -o $name $LIBFUSE_DIR/lib/libfuse.a  -lpthread -ldl $opt
+
+if [ -e $name ]; then
+   echo -e "\n############### SUCCESS $name ##################\n"
+else
+    echo -e "\n############### FAILED $name ##################\n"
+fi
+
+strip --strip-all $name
+
diff --git a/FUSEISO/build_libfuse.sh b/FUSEISO/build_libfuse.sh
new file mode 100644 (file)
index 0000000..25ef02a
--- /dev/null
@@ -0,0 +1,33 @@
+#!/bin/bash
+
+#
+#
+# Package Dependency:
+# gcc automake autoconf gettext gettext-devel libtool unzip
+#
+#
+
+
+CUR="$PWD"
+LIBFUSE_DIR=$CUR/LIBFUSE
+
+rm -rf libfuse
+rm -rf $LIBFUSE_DIR
+
+# please download https://gitee.com/mirrors/libfuse/repository/archive/fuse-2.9.9.zip
+if ! [ -e mirrors-libfuse-fuse-2.9.9.zip ]; then
+    echo "Please download mirrors-libfuse-fuse-2.9.9.zip first"
+    exit 1
+fi
+
+unzip mirrors-libfuse-fuse-2.9.9.zip
+
+
+cd libfuse
+./makeconf.sh
+
+./configure --prefix="$LIBFUSE_DIR"
+make -j 16
+make install
+cd ..
+rm -rf libfuse
diff --git a/FUSEISO/vtoy_fuse_iso.c b/FUSEISO/vtoy_fuse_iso.c
new file mode 100644 (file)
index 0000000..0350ea0
--- /dev/null
@@ -0,0 +1,346 @@
+/******************************************************************************
+ * vtoy_fuse_iso.c
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#define FUSE_USE_VERSION 26
+
+#include <fuse.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <fcntl.h>
+
+typedef unsigned int uint32_t;
+
+typedef struct dmtable_entry
+{
+    uint32_t isoSector;
+    uint32_t sectorNum;
+    unsigned long long diskSector;
+}dmtable_entry;
+
+#define MAX_ENTRY_NUM  (1024 * 1024 / sizeof(dmtable_entry))
+
+static int verbose = 0;
+#define debug(fmt, ...) if(verbose) printf(fmt, ##__VA_ARGS__)
+
+static int g_disk_fd = -1;
+static uint64_t g_iso_file_size;
+static char g_mnt_point[512];
+static char g_iso_file_name[512];
+static dmtable_entry *g_disk_entry_list = NULL;
+static int g_disk_entry_num = 0;
+
+static int ventoy_iso_getattr(const char *path, struct stat *statinfo)
+{
+    int ret = -ENOENT;
+
+    if (path && statinfo)
+    {
+        memset(statinfo, 0, sizeof(struct stat));
+
+        if (path[0] == '/' && path[1] == 0)
+        {
+            statinfo->st_mode  = S_IFDIR | 0755;
+            statinfo->st_nlink = 2;
+            ret = 0;
+        }
+        else if (strcmp(path, g_iso_file_name) == 0)
+        {
+            statinfo->st_mode  = S_IFREG | 0444;
+            statinfo->st_nlink = 1;
+            statinfo->st_size  = g_iso_file_size;
+            ret = 0;
+        }
+    }
+    
+    return ret;
+}
+
+static int ventoy_iso_readdir
+(
+    const char *path, 
+    void *buf, 
+    fuse_fill_dir_t filler,
+    off_t offset, 
+    struct fuse_file_info *file
+)
+{
+    (void)offset;
+    (void)file;
+
+    if (path[0] != '/' || path[1] != 0)
+    {
+        return -ENOENT;
+    }
+
+    filler(buf, ".", NULL, 0);
+    filler(buf, "..", NULL, 0);
+    filler(buf, g_iso_file_name + 1, NULL, 0);
+
+    return 0;
+}
+
+static int ventoy_iso_open(const char *path, struct fuse_file_info *file)
+{
+    if (strcmp(path, g_iso_file_name) != 0)
+    {
+        return -ENOENT;
+    }
+
+    if ((file->flags & 3) != O_RDONLY)
+    {
+        return -EACCES;
+    }
+
+    return 0;
+}
+
+static int ventoy_read_iso_sector(uint32_t sector, uint32_t num, void *buf)
+{
+    uint32_t i = 0;
+    uint32_t leftSec = 0;
+    uint32_t readSec = 0;
+    dmtable_entry *entry = NULL;
+    
+    for (i = 0; i < g_disk_entry_num && num > 0; i++)
+    {
+        entry = g_disk_entry_list + i;
+
+        if (sector >= entry->isoSector && sector < entry->isoSector + entry->sectorNum)
+        {
+            lseek(g_disk_fd, (entry->diskSector + (sector - entry->isoSector)) * 512, SEEK_SET);
+
+            leftSec = entry->sectorNum - (sector - entry->isoSector);
+            readSec = (leftSec > num) ? num : leftSec;
+
+            read(g_disk_fd, buf, readSec * 512);
+
+            sector += readSec;
+            num -= readSec;
+        }
+    }
+
+    return 0;
+}
+
+static int ventoy_iso_read
+(
+    const char *path, char *buf, 
+    size_t size, off_t offset,
+    struct fuse_file_info *file
+)
+{
+    uint32_t mod = 0;
+    uint32_t align = 0;
+    uint32_t sector = 0;
+    uint32_t number = 0;
+    size_t leftsize = 0;
+    char secbuf[512];
+    
+    (void)file;
+    
+    if(strcmp(path, g_iso_file_name) != 0)
+    {
+        return -ENOENT;        
+    }
+
+    if (offset >= g_iso_file_size)
+    {
+        return 0;
+    }
+
+    if (offset + size > g_iso_file_size)
+    {
+        size = g_iso_file_size - offset;
+    }
+    
+    leftsize = size;
+    sector = offset / 512;
+
+    mod = offset % 512;
+    if (mod > 0)
+    {
+        align = 512 - mod;
+        ventoy_read_iso_sector(sector, 1, secbuf);
+
+        if (leftsize > align)
+        {
+            memcpy(buf, secbuf + mod, align);
+            buf += align;
+            offset += align;
+            sector++;
+            leftsize -= align;
+        }
+        else
+        {
+            memcpy(buf, secbuf + mod, leftsize);
+            return size;
+        }
+    }
+
+    number = leftsize / 512;
+    ventoy_read_iso_sector(sector, number, buf);
+    buf += number * 512;
+
+    mod = leftsize % 512;
+    if (mod > 0)
+    {
+        ventoy_read_iso_sector(sector + number, 1, secbuf);
+        memcpy(buf, secbuf, mod);
+    }
+
+    return size;
+}
+
+static struct fuse_operations ventoy_op = 
+{
+    .getattr    = ventoy_iso_getattr,
+    .readdir    = ventoy_iso_readdir,
+    .open       = ventoy_iso_open,
+    .read       = ventoy_iso_read,
+};
+
+static int ventoy_parse_dmtable(const char *filename)
+{
+    FILE *fp = NULL;
+    char diskname[128] = {0};
+    char line[256] = {0};
+    dmtable_entry *entry= g_disk_entry_list;
+
+    fp = fopen(filename, "r");
+    if (NULL == fp)
+    {
+        printf("Failed to open file %s\n", filename);
+        return 1;
+    }
+
+    /* read untill the last line */
+    while (fgets(line, sizeof(line), fp) && g_disk_entry_num < MAX_ENTRY_NUM)
+    {
+        sscanf(line, "%u %u linear %s %llu", 
+               &entry->isoSector, &entry->sectorNum, 
+               diskname, &entry->diskSector);
+
+        g_iso_file_size += (uint64_t)entry->sectorNum * 512ULL;
+        g_disk_entry_num++;
+        entry++;
+    }
+    fclose(fp);
+
+    if (g_disk_entry_num >= MAX_ENTRY_NUM)
+    {
+        fprintf(stderr, "ISO file has too many fragments ( more than %u )\n", MAX_ENTRY_NUM);
+        return 1;
+    }
+
+    debug("iso file size: %llu disk name %s\n", g_iso_file_size, diskname);
+
+    g_disk_fd = open(diskname, O_RDONLY);
+    if (g_disk_fd < 0)
+    {
+        debug("Failed to open %s\n", diskname);
+        return 1;
+    }
+
+    return 0;
+}
+
+int main(int argc, char **argv)
+{
+    int rc;
+    int ch;
+    char filename[512] = {0};
+
+    /* Avoid to be killed by systemd */
+    if (access("/etc/initrd-release", F_OK) >= 0)
+    {          
+        argv[0][0] = '@';
+    }
+
+    g_iso_file_name[0] = '/';
+    
+    while ((ch = getopt(argc, argv, "f:s:m:v::t::")) != -1)
+    {
+        if (ch == 'f')
+        {
+            strncpy(filename, optarg, sizeof(filename) - 1);
+        }
+        else if (ch == 'm')
+        {
+            strncpy(g_mnt_point, optarg, sizeof(g_mnt_point) - 1);
+        }
+        else if (ch == 's')
+        {
+            strncpy(g_iso_file_name + 1, optarg, sizeof(g_iso_file_name) - 2);
+        }
+        else if (ch == 'v')
+        {
+            verbose = 1;
+        }
+        else if (ch == 't') // for test
+        {
+            return 0;
+        }
+    }
+
+    if (filename[0] == 0)
+    {
+        fprintf(stderr, "Must input dmsetup table file with -f\n");
+        return 1;
+    }
+
+    if (g_mnt_point[0] == 0)
+    {
+        fprintf(stderr, "Must input mount point with -m\n");
+        return 1;
+    }
+
+    if (g_iso_file_name[1] == 0)
+    {
+        strncpy(g_iso_file_name + 1, "ventoy.iso", sizeof(g_iso_file_name) - 2);
+    }
+
+    debug("ventoy fuse iso: %s %s %s\n", filename, g_iso_file_name, g_mnt_point);
+
+    g_disk_entry_list = malloc(MAX_ENTRY_NUM * sizeof(dmtable_entry));
+    if (NULL == g_disk_entry_list)
+    {
+        return 1;
+    }
+
+    rc = ventoy_parse_dmtable(filename);
+    if (rc)
+    {
+        free(g_disk_entry_list);
+        return rc;
+    }
+
+    argv[1] = g_mnt_point;
+    argv[2] = NULL;
+    rc = fuse_main(2, argv, &ventoy_op, NULL);
+
+    close(g_disk_fd);
+
+    free(g_disk_entry_list);
+    return rc;
+}
+
diff --git a/LICENSE b/GPLv3
similarity index 100%
rename from LICENSE
rename to GPLv3
diff --git a/GRUB2/README.txt b/GRUB2/README.txt
new file mode 100644 (file)
index 0000000..57f0008
--- /dev/null
@@ -0,0 +1,14 @@
+
+========== About Source Code =============
+Ventoy use grub-2.04, so I only put the added and modified source code here.
+
+You can download grub-2.04 source code from this site:
+https://ftp.gnu.org/gnu/grub/
+
+Just merge the code here with the original code of grub-2.04
+
+
+========== Build =============
+./autogen.sh
+./configure 
+make
diff --git a/GRUB2/grub-2.04/grub-core/Makefile.core.def b/GRUB2/grub-2.04/grub-core/Makefile.core.def
new file mode 100644 (file)
index 0000000..d0455f9
--- /dev/null
@@ -0,0 +1,2517 @@
+AutoGen definitions Makefile.tpl;
+
+transform_data = {
+  installdir = noinst;
+  name = gensyminfo.sh;
+  common = gensyminfo.sh.in;
+};
+
+transform_data = {
+  installdir = noinst;
+  name = genmod.sh;
+  common = genmod.sh.in;
+};
+
+transform_data = {
+  installdir = noinst;
+  name = modinfo.sh;
+  common = modinfo.sh.in;
+};
+
+transform_data = {
+  installdir = platform;
+  name = gmodule.pl;
+  common = gmodule.pl.in;
+};
+
+transform_data = {
+  installdir = platform;
+  name = gdb_grub;
+  common = gdb_grub.in;
+};
+
+transform_data = {
+  installdir = platform;
+  name = grub.chrp;
+  common = boot/powerpc/grub.chrp.in;
+  enable = powerpc_ieee1275;
+};
+
+transform_data = {
+  installdir = platform;
+  name = bootinfo.txt;
+  common = boot/powerpc/bootinfo.txt.in;
+  enable = powerpc_ieee1275;
+};
+
+kernel = {
+  name = kernel;
+
+  nostrip = emu;
+
+  emu_ldflags              = '-Wl,-r,-d';
+  i386_efi_ldflags         = '-Wl,-r,-d';
+  i386_efi_stripflags      = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version';
+  x86_64_efi_ldflags       = '-Wl,-r,-d';
+  x86_64_efi_stripflags    = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version';
+
+  ia64_efi_cflags = '-fno-builtin -fpic -minline-int-divide-max-throughput';
+  ia64_efi_ldflags = '-Wl,-r,-d';
+  ia64_efi_stripflags = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version';
+
+  arm_efi_ldflags          = '-Wl,-r,-d';
+  arm_efi_stripflags       = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version';
+
+  arm64_efi_ldflags          = '-Wl,-r,-d';
+  arm64_efi_stripflags       = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version -R .eh_frame';
+
+  riscv32_efi_ldflags      = '-Wl,-r,-d';
+  riscv32_efi_stripflags   = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version -R .eh_frame';
+
+  riscv64_efi_ldflags      = '-Wl,-r,-d';
+  riscv64_efi_stripflags   = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version -R .eh_frame';
+
+  i386_pc_ldflags          = '$(TARGET_IMG_LDFLAGS)';
+  i386_pc_ldflags          = '$(TARGET_IMG_BASE_LDOPT),0x9000';
+  i386_qemu_ldflags        = '$(TARGET_IMG_LDFLAGS)';
+  i386_qemu_ldflags        = '$(TARGET_IMG_BASE_LDOPT),0x9000';
+  i386_coreboot_ldflags    = '$(TARGET_IMG_LDFLAGS)';
+  i386_coreboot_ldflags    = '$(TARGET_IMG_BASE_LDOPT),0x9000';
+  i386_multiboot_ldflags   = '$(TARGET_IMG_LDFLAGS)';
+  i386_multiboot_ldflags   = '$(TARGET_IMG_BASE_LDOPT),0x9000';
+  i386_ieee1275_ldflags    = '$(TARGET_IMG_LDFLAGS)';
+  i386_ieee1275_ldflags    = '$(TARGET_IMG_BASE_LDOPT),0x10000';
+  i386_xen_ldflags         = '$(TARGET_IMG_LDFLAGS)';
+  i386_xen_ldflags         = '$(TARGET_IMG_BASE_LDOPT),0';
+  x86_64_xen_ldflags       = '$(TARGET_IMG_LDFLAGS)';
+  x86_64_xen_ldflags       = '$(TARGET_IMG_BASE_LDOPT),0';
+  i386_xen_pvh_ldflags     = '$(TARGET_IMG_LDFLAGS)';
+  i386_xen_pvh_ldflags     = '$(TARGET_IMG_BASE_LDOPT),0x100000';
+
+  mips_loongson_ldflags    = '-Wl,-Ttext,0x80200000';
+  powerpc_ieee1275_ldflags = '-Wl,-Ttext,0x200000';
+  sparc64_ieee1275_ldflags = '-Wl,-Ttext,0x4400';
+  mips_arc_ldflags    = '-Wl,-Ttext,$(TARGET_LINK_ADDR)';
+  mips_qemu_mips_ldflags    = '-Wl,-Ttext,0x80200000';
+
+  mips_arc_cppflags = '-DGRUB_DECOMPRESSOR_LINK_ADDR=$(TARGET_DECOMPRESSOR_LINK_ADDR)';
+  i386_qemu_cppflags     = '-DGRUB_BOOT_MACHINE_LINK_ADDR=$(GRUB_BOOT_MACHINE_LINK_ADDR)';
+  emu_cflags = '$(CFLAGS_GNULIB)';
+  emu_cppflags = '$(CPPFLAGS_GNULIB)';
+  arm_uboot_ldflags       = '-Wl,-r,-d';
+  arm_uboot_stripflags    = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version';
+  arm_coreboot_ldflags       = '-Wl,-r,-d';
+  arm_coreboot_stripflags    = '--strip-unneeded -K start -R .note -R .comment -R .note.gnu.gold-version';
+
+  i386_pc_startup = kern/i386/pc/startup.S;
+  i386_efi_startup = kern/i386/efi/startup.S;
+  x86_64_efi_startup = kern/x86_64/efi/startup.S;
+  i386_xen_startup = kern/i386/xen/startup.S;
+  x86_64_xen_startup = kern/x86_64/xen/startup.S;
+  i386_xen_pvh_startup = kern/i386/xen/startup_pvh.S;
+  i386_qemu_startup = kern/i386/qemu/startup.S;
+  i386_ieee1275_startup = kern/i386/ieee1275/startup.S;
+  i386_coreboot_startup = kern/i386/coreboot/startup.S;
+  i386_multiboot_startup = kern/i386/coreboot/startup.S;
+  mips_startup = kern/mips/startup.S;
+  sparc64_ieee1275_startup = kern/sparc64/ieee1275/crt0.S;
+  powerpc_ieee1275_startup = kern/powerpc/ieee1275/startup.S;
+  arm_uboot_startup = kern/arm/startup.S;
+  arm_coreboot_startup = kern/arm/startup.S;
+  arm_efi_startup = kern/arm/efi/startup.S;
+  arm64_efi_startup = kern/arm64/efi/startup.S;
+  riscv32_efi_startup = kern/riscv/efi/startup.S;
+  riscv64_efi_startup = kern/riscv/efi/startup.S;
+
+  common = kern/command.c;
+  common = kern/corecmd.c;
+  common = kern/device.c;
+  common = kern/disk.c;
+  common = kern/dl.c;
+  common = kern/env.c;
+  common = kern/err.c;
+  common = kern/file.c;
+  common = kern/fs.c;
+  common = kern/list.c;
+  common = kern/main.c;
+  common = kern/misc.c;
+  common = kern/parser.c;
+  common = kern/partition.c;
+  common = kern/rescue_parser.c;
+  common = kern/rescue_reader.c;
+  common = kern/term.c;
+
+  noemu = kern/compiler-rt.c;
+  noemu = kern/mm.c;
+  noemu = kern/time.c;
+  noemu = kern/generic/millisleep.c;
+
+  noemu_nodist = symlist.c;
+
+  mips = kern/generic/rtc_get_time_ms.c;
+
+  ieee1275 = disk/ieee1275/ofdisk.c;
+  ieee1275 = kern/ieee1275/cmain.c;
+  ieee1275 = kern/ieee1275/ieee1275.c;
+  ieee1275 = kern/ieee1275/mmap.c;
+  ieee1275 = kern/ieee1275/openfw.c;
+  ieee1275 = term/ieee1275/console.c;
+  ieee1275 = kern/ieee1275/init.c;
+
+  uboot = disk/uboot/ubootdisk.c;
+  uboot = kern/uboot/uboot.c;
+  uboot = kern/uboot/init.c;
+  uboot = kern/uboot/hw.c;
+  uboot = term/uboot/console.c;
+  arm_uboot = kern/arm/uboot/init.c;
+  arm_uboot = kern/arm/uboot/uboot.S;
+
+  arm_coreboot = kern/arm/coreboot/init.c;
+  arm_coreboot = kern/arm/coreboot/timer.c;
+  arm_coreboot = kern/arm/coreboot/coreboot.S;
+  arm_coreboot = lib/fdt.c;
+  arm_coreboot = bus/fdt.c;
+  arm_coreboot = term/ps2.c;
+  arm_coreboot = term/arm/pl050.c;
+  arm_coreboot = term/arm/cros.c;
+  arm_coreboot = term/arm/cros_ec.c;
+  arm_coreboot = bus/spi/rk3288_spi.c;
+  arm_coreboot = commands/keylayouts.c;
+  arm_coreboot = kern/arm/coreboot/dma.c;
+
+  terminfoinkernel = term/terminfo.c;
+  terminfoinkernel = term/tparm.c;
+  terminfoinkernel = commands/extcmd.c;
+  terminfoinkernel = lib/arg.c;
+
+  softdiv = lib/division.c;
+
+  i386 = kern/i386/dl.c;
+  i386_xen = kern/i386/dl.c;
+  i386_xen_pvh = kern/i386/dl.c;
+
+  i386_coreboot = kern/i386/coreboot/init.c;
+  i386_multiboot = kern/i386/coreboot/init.c;
+  i386_qemu = kern/i386/qemu/init.c;
+  i386_coreboot_multiboot_qemu = term/i386/pc/vga_text.c;
+  coreboot = video/coreboot/cbfb.c;
+
+  efi = disk/efi/efidisk.c;
+  efi = kern/efi/efi.c;
+  efi = kern/efi/init.c;
+  efi = kern/efi/mm.c;
+  efi = term/efi/console.c;
+  efi = kern/acpi.c;
+  efi = kern/efi/acpi.c;
+  i386_coreboot = kern/i386/pc/acpi.c;
+  i386_multiboot = kern/i386/pc/acpi.c;
+  i386_coreboot = kern/acpi.c;
+  i386_multiboot = kern/acpi.c;
+
+  x86 = kern/i386/tsc.c;
+  x86 = kern/i386/tsc_pit.c;
+  i386_efi = kern/i386/efi/tsc.c;
+  x86_64_efi = kern/i386/efi/tsc.c;
+  i386_efi = kern/i386/tsc_pmtimer.c;
+  i386_coreboot = kern/i386/tsc_pmtimer.c;
+  x86_64_efi = kern/i386/tsc_pmtimer.c;
+
+  i386_efi = kern/i386/efi/init.c;
+  i386_efi = bus/pci.c;
+
+  x86_64 = kern/x86_64/dl.c;
+  x86_64_xen = kern/x86_64/dl.c;
+  x86_64_efi = kern/x86_64/efi/callwrap.S;
+  x86_64_efi = kern/i386/efi/init.c;
+  x86_64_efi = bus/pci.c;
+
+  xen = kern/i386/tsc.c;
+  xen = kern/i386/xen/tsc.c;
+  x86_64_xen = kern/x86_64/xen/hypercall.S;
+  i386_xen = kern/i386/xen/hypercall.S;
+  xen = kern/xen/init.c;
+  xen = term/xen/console.c;
+  xen = disk/xen/xendisk.c;
+  xen = commands/boot.c;
+
+  i386_xen_pvh = commands/boot.c;
+  i386_xen_pvh = disk/xen/xendisk.c;
+  i386_xen_pvh = kern/i386/tsc.c;
+  i386_xen_pvh = kern/i386/xen/tsc.c;
+  i386_xen_pvh = kern/i386/xen/pvh.c;
+  i386_xen_pvh = kern/xen/init.c;
+  i386_xen_pvh = term/xen/console.c;
+
+  ia64_efi = kern/ia64/efi/startup.S;
+  ia64_efi = kern/ia64/efi/init.c;
+  ia64_efi = kern/ia64/dl.c;
+  ia64_efi = kern/ia64/dl_helper.c;
+  ia64_efi = kern/ia64/cache.c;
+
+  arm_efi = kern/arm/efi/init.c;
+  arm_efi = kern/efi/fdt.c;
+
+  arm64_efi = kern/arm64/efi/init.c;
+  arm64_efi = kern/efi/fdt.c;
+
+  riscv32_efi = kern/riscv/efi/init.c;
+  riscv32_efi = kern/efi/fdt.c;
+
+  riscv64_efi = kern/riscv/efi/init.c;
+  riscv64_efi = kern/efi/fdt.c;
+
+  i386_pc = kern/i386/pc/init.c;
+  i386_pc = kern/i386/pc/mmap.c;
+  i386_pc = term/i386/pc/console.c;
+
+  i386_qemu = bus/pci.c;
+  i386_qemu = kern/vga_init.c;
+  i386_qemu = kern/i386/qemu/mmap.c;
+
+  coreboot = kern/coreboot/mmap.c;
+  i386_coreboot = kern/i386/coreboot/cbtable.c;
+  coreboot = kern/coreboot/cbtable.c;
+  arm_coreboot = kern/arm/coreboot/cbtable.c;
+
+  i386_multiboot = kern/i386/multiboot_mmap.c;
+
+  mips = kern/mips/cache.S;
+  mips = kern/mips/dl.c;
+  mips = kern/mips/init.c;
+
+  mips_qemu_mips = kern/mips/qemu_mips/init.c;
+  mips_qemu_mips = term/ns8250.c;
+  mips_qemu_mips = term/serial.c;
+  mips_qemu_mips = term/at_keyboard.c;
+  mips_qemu_mips = term/ps2.c;
+  mips_qemu_mips = commands/boot.c;
+  mips_qemu_mips = commands/keylayouts.c;
+  mips_qemu_mips = term/i386/pc/vga_text.c;
+  mips_qemu_mips = kern/vga_init.c;
+
+  mips_arc = kern/mips/arc/init.c;
+  mips_arc = term/arc/console.c;
+  mips_arc = disk/arc/arcdisk.c;
+
+  mips_loongson = term/ns8250.c;
+  mips_loongson = bus/bonito.c;
+  mips_loongson = bus/cs5536.c;
+  mips_loongson = bus/pci.c;
+  mips_loongson = kern/mips/loongson/init.c;
+  mips_loongson = term/at_keyboard.c;
+  mips_loongson = term/ps2.c;
+  mips_loongson = commands/boot.c;
+  mips_loongson = term/serial.c;
+  mips_loongson = video/sm712.c;
+  mips_loongson = video/sis315pro.c;
+  mips_loongson = video/radeon_fuloong2e.c;
+  mips_loongson = video/radeon_yeeloong3a.c;
+  extra_dist = video/sm712_init.c;
+  extra_dist = video/sis315_init.c;
+  mips_loongson = commands/keylayouts.c;
+
+  powerpc_ieee1275 = kern/powerpc/cache.S;
+  powerpc_ieee1275 = kern/powerpc/dl.c;
+  powerpc_ieee1275 = kern/powerpc/compiler-rt.S;
+
+  sparc64_ieee1275 = kern/sparc64/cache.S;
+  sparc64_ieee1275 = kern/sparc64/dl.c;
+  sparc64_ieee1275 = kern/sparc64/ieee1275/ieee1275.c;
+  sparc64_ieee1275 = disk/ieee1275/obdisk.c;
+
+  arm = kern/arm/dl.c;
+  arm = kern/arm/dl_helper.c;
+  arm = kern/arm/cache_armv6.S;
+  arm = kern/arm/cache_armv7.S;
+  extra_dist = kern/arm/cache.S;
+  arm = kern/arm/cache.c;
+  arm = kern/arm/compiler-rt.S;
+
+  arm64 = kern/arm64/cache.c;
+  arm64 = kern/arm64/cache_flush.S;
+  arm64 = kern/arm64/dl.c;
+  arm64 = kern/arm64/dl_helper.c;
+
+  riscv32 = kern/riscv/cache.c;
+  riscv32 = kern/riscv/cache_flush.S;
+  riscv32 = kern/riscv/dl.c;
+
+  riscv64 = kern/riscv/cache.c;
+  riscv64 = kern/riscv/cache_flush.S;
+  riscv64 = kern/riscv/dl.c;
+
+  emu = disk/host.c;
+  emu = kern/emu/cache_s.S;
+  emu = kern/emu/hostdisk.c;
+  emu = osdep/unix/hostdisk.c;
+  emu = osdep/exec.c;
+  extra_dist = osdep/unix/exec.c;
+  emu = osdep/devmapper/hostdisk.c;
+  emu = osdep/hostdisk.c;
+  emu = kern/emu/hostfs.c;
+  emu = kern/emu/main.c;
+  emu = kern/emu/argp_common.c;
+  emu = kern/emu/misc.c;
+  emu = kern/emu/mm.c;
+  emu = kern/emu/time.c;
+  emu = kern/emu/cache.c;
+  emu = osdep/emuconsole.c;
+  extra_dist = osdep/unix/emuconsole.c;
+  extra_dist = osdep/windows/emuconsole.c;
+  emu = osdep/dl.c;
+  extra_dist = osdep/unix/dl.c;
+  extra_dist = osdep/windows/dl.c;
+  emu = osdep/sleep.c;
+  emu = osdep/init.c;
+  emu = osdep/emunet.c;
+  extra_dist = osdep/linux/emunet.c;
+  extra_dist = osdep/basic/emunet.c;
+  emu = osdep/cputime.c;
+  extra_dist = osdep/unix/cputime.c;
+  extra_dist = osdep/windows/cputime.c;
+
+  videoinkernel = term/gfxterm.c;
+  videoinkernel = font/font.c;
+  videoinkernel = font/font_cmd.c;
+  videoinkernel = io/bufio.c;
+  videoinkernel = video/fb/fbblit.c;
+  videoinkernel = video/fb/fbfill.c;
+  videoinkernel = video/fb/fbutil.c;
+  videoinkernel = video/fb/video_fb.c;
+  videoinkernel = video/video.c;
+
+  extra_dist = kern/i386/int.S;
+  extra_dist = kern/i386/realmode.S;
+  extra_dist = boot/i386/pc/lzma_decode.S;
+  extra_dist = kern/mips/cache_flush.S;
+};
+
+program = {
+  name = grub-emu;
+  mansection = 1;
+
+  emu = kern/emu/full.c;
+  emu_nodist = grub_emu_init.c;
+
+  ldadd = 'kernel.exec$(EXEEXT)';
+  ldadd = '$(MODULE_FILES)';
+  ldadd = 'lib/gnulib/libgnu.a $(LIBINTL) $(LIBUTIL) $(LIBSDL) $(LIBUSB) $(LIBPCIACCESS) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
+
+  enable = emu;
+};
+
+program = {
+  name = grub-emu-lite;
+
+  emu = kern/emu/lite.c;
+  emu_nodist = symlist.c;
+
+  ldadd = 'kernel.exec$(EXEEXT)';
+  ldadd = 'lib/gnulib/libgnu.a $(LIBINTL) $(LIBUTIL) $(LIBSDL) $(LIBUSB) $(LIBPCIACCESS) $(LIBDEVMAPPER) $(LIBZFS) $(LIBNVPAIR) $(LIBGEOM)';
+
+  enable = emu;
+};
+
+image = {
+  name = boot;
+  i386_pc = boot/i386/pc/boot.S;
+  i386_qemu = boot/i386/qemu/boot.S;
+  sparc64_ieee1275 = boot/sparc64/ieee1275/boot.S;
+
+  i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)';
+  i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x7C00';
+
+  i386_qemu_ldflags = '$(TARGET_IMG_LDFLAGS)';
+  i386_qemu_ldflags = '$(TARGET_IMG_BASE_LDOPT),$(GRUB_BOOT_MACHINE_LINK_ADDR)';
+  i386_qemu_ccasflags = '-DGRUB_BOOT_MACHINE_LINK_ADDR=$(GRUB_BOOT_MACHINE_LINK_ADDR)';
+
+  /* The entry point for a.out binaries on sparc64 starts
+     at 0x4000. Since we are writing the 32 bytes long a.out
+     header in the assembly code ourselves, we need to tell
+     the linker to adjust the start of the text segment to
+     0x4000 - 0x20 = 0x3fe0.
+   */
+  sparc64_ieee1275_ldflags = ' -Wl,-Ttext=0x3fe0';
+  sparc64_ieee1275_objcopyflags = '-O binary';
+
+  objcopyflags = '-O binary';
+  enable = i386_pc;
+  enable = i386_qemu;
+  enable = sparc64_ieee1275;
+};
+
+image = {
+  name = boot_hybrid;
+  i386_pc = boot/i386/pc/boot.S;
+
+  cppflags = '-DHYBRID_BOOT=1';
+  
+  i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)';
+  i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x7C00';
+
+  objcopyflags = '-O binary';
+  enable = i386_pc;
+};
+
+image = {
+  name = cdboot;
+
+  i386_pc = boot/i386/pc/cdboot.S;
+  i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)';
+  i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x7C00';
+
+  sparc64_ieee1275 = boot/sparc64/ieee1275/boot.S;
+
+  /* See comment for sparc64_ieee1275_ldflags above. */
+  sparc64_ieee1275_ldflags = ' -Wl,-Ttext=0x3fe0';
+  sparc64_ieee1275_objcopyflags = '-O binary';
+  sparc64_ieee1275_cppflags = '-DCDBOOT=1';
+
+  objcopyflags = '-O binary';
+
+  enable = sparc64_ieee1275;
+  enable = i386_pc;
+};
+
+image = {
+  name = pxeboot;
+  i386_pc = boot/i386/pc/pxeboot.S;
+
+  i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)';
+  i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x7C00';
+
+  objcopyflags = '-O binary';
+  enable = i386_pc;
+};
+
+image = {
+  name = diskboot;
+  i386_pc = boot/i386/pc/diskboot.S;
+
+  i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)';
+  i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x8000';
+
+  sparc64_ieee1275 = boot/sparc64/ieee1275/diskboot.S;
+  sparc64_ieee1275_ldflags = '-Wl,-Ttext=0x4200';
+
+  objcopyflags = '-O binary';
+
+  enable = i386_pc;
+  enable = sparc64_ieee1275;
+};
+
+image = {
+  name = lnxboot;
+  i386_pc = boot/i386/pc/lnxboot.S;
+
+  i386_pc_ldflags = '$(TARGET_IMG_LDFLAGS)';
+  i386_pc_ldflags = '$(TARGET_IMG_BASE_LDOPT),0x6000';
+
+  objcopyflags = '-O binary';
+  enable = i386_pc;
+};
+
+image = {
+  name = xz_decompress;
+  mips = boot/mips/startup_raw.S;
+  common = boot/decompressor/minilib.c;
+  common = boot/decompressor/xz.c;
+  common = lib/xzembed/xz_dec_bcj.c;
+  common = lib/xzembed/xz_dec_lzma2.c;
+  common = lib/xzembed/xz_dec_stream.c;
+  common = kern/compiler-rt.c;
+
+  cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/xzembed -DGRUB_EMBED_DECOMPRESSOR=1';
+
+  objcopyflags = '-O binary';
+  mips_ldflags = '-Wl,-Ttext,$(TARGET_DECOMPRESSOR_LINK_ADDR)';
+  cflags = '-Wno-unreachable-code';
+  enable = mips;
+};
+
+image = {
+  name = none_decompress;
+  mips = boot/mips/startup_raw.S;
+  common = boot/decompressor/none.c;
+
+  cppflags = '-DGRUB_EMBED_DECOMPRESSOR=1';
+
+  objcopyflags = '-O binary';
+  mips_ldflags = '-Wl,-Ttext,$(TARGET_DECOMPRESSOR_LINK_ADDR)';
+  enable = mips;
+};
+
+image = {
+  name = lzma_decompress;
+  i386_pc = boot/i386/pc/startup_raw.S;
+  i386_pc_nodist = rs_decoder.h;
+
+  objcopyflags = '-O binary';
+  ldflags = '$(TARGET_IMG_LDFLAGS) $(TARGET_IMG_BASE_LDOPT),0x8200';
+  enable = i386_pc;
+};
+
+image = {
+  name = fwstart;
+  mips_loongson = boot/mips/loongson/fwstart.S;
+  objcopyflags = '-O binary';
+  ldflags = '-Wl,-N,-S,-Ttext,0xbfc00000,-Bstatic';
+  enable = mips_loongson;
+};
+
+image = {
+  name = fwstart_fuloong2f;
+  mips_loongson = boot/mips/loongson/fuloong2f.S;
+  objcopyflags = '-O binary';
+  ldflags = '-Wl,-N,-S,-Ttext,0xbfc00000,-Bstatic';
+  enable = mips_loongson;
+};
+
+module = {
+  name = disk;
+  common = lib/disk.c;
+  extra_dist = kern/disk_common.c;
+};
+
+module = {
+  name = trig;
+  common_nodist = trigtables.c;
+  extra_dist = gentrigtables.c;
+};
+
+module = {
+  name = cs5536;
+  x86 = bus/cs5536.c;
+  enable = x86;
+};
+
+module = {
+  name = lsspd;
+  mips_loongson = commands/mips/loongson/lsspd.c;
+  enable = mips_loongson;
+};
+
+module = {
+  name = usb;
+  common = bus/usb/usb.c;
+  common = bus/usb/usbtrans.c;
+  common = bus/usb/usbhub.c;
+  enable = usb;
+};
+
+module = {
+  name = usbserial_common;
+  common = bus/usb/serial/common.c;
+  enable = usb;
+};
+
+module = {
+  name = usbserial_pl2303;
+  common = bus/usb/serial/pl2303.c;
+  enable = usb;
+};
+
+module = {
+  name = usbserial_ftdi;
+  common = bus/usb/serial/ftdi.c;
+  enable = usb;
+};
+
+module = {
+  name = usbserial_usbdebug;
+  common = bus/usb/serial/usbdebug_late.c;
+  enable = usb;
+};
+
+module = {
+  name = uhci;
+  common = bus/usb/uhci.c;
+  enable = pci;
+};
+
+module = {
+  name = ohci;
+  common = bus/usb/ohci.c;
+  enable = pci;
+};
+
+module = {
+  name = ehci;
+  common = bus/usb/ehci.c;
+  arm_coreboot = bus/usb/ehci-fdt.c;
+  pci = bus/usb/ehci-pci.c;
+  enable = pci;
+  enable = arm_coreboot;
+};
+
+module = {
+  name = pci;
+  common = bus/pci.c;
+  i386_ieee1275 = bus/i386/ieee1275/pci.c;
+
+  enable = i386_pc;
+  enable = i386_ieee1275;
+  enable = i386_coreboot;
+  enable = i386_multiboot;
+};
+
+module = {
+  name = nativedisk;
+  common = commands/nativedisk.c;
+
+  enable = x86;
+  enable = mips_loongson;
+  enable = mips_qemu_mips;
+};
+
+module = {
+  name = emupci;
+  common = bus/emu/pci.c;
+  common = commands/lspci.c;
+
+  enable = emu;
+  condition = COND_GRUB_EMU_PCI;
+};
+
+module = {
+  name = lsdev;
+  common = commands/arc/lsdev.c;
+
+  enable = mips_arc;
+};
+
+module = {
+  name = lsxen;
+  common = commands/xen/lsxen.c;
+
+  enable = xen;
+};
+
+module = {
+  name = cmostest;
+  common = commands/i386/cmostest.c;
+  enable = cmos;
+};
+
+module = {
+  name = cmosdump;
+  common = commands/i386/cmosdump.c;
+  enable = cmos;
+};
+
+module = {
+  name = iorw;
+  common = commands/iorw.c;
+  enable = x86;
+};
+
+module = {
+  name = cbtable;
+  common = kern/i386/coreboot/cbtable.c;
+  common = kern/coreboot/cbtable.c;
+  enable = i386_pc;
+  enable = i386_efi;
+  enable = i386_qemu;
+  enable = i386_multiboot;
+  enable = i386_ieee1275;
+  enable = x86_64_efi;
+};
+
+module = {
+  name = cbtime;
+  common = commands/i386/coreboot/cb_timestamps.c;
+  enable = x86;
+};
+
+module = {
+  name = cbls;
+  common = commands/i386/coreboot/cbls.c;
+  enable = x86;
+};
+
+module = {
+  name = cbmemc;
+  common = term/i386/coreboot/cbmemc.c;
+  enable = x86;
+};
+
+module = {
+  name = regexp;
+  common = commands/regexp.c;
+  common = commands/wildcard.c;
+  common = lib/gnulib/regex.c;
+  cflags = '$(CFLAGS_POSIX) $(CFLAGS_GNULIB)';
+  cppflags = '$(CPPFLAGS_POSIX) $(CPPFLAGS_GNULIB)';
+};
+
+module = {
+  name = acpi;
+
+  common = commands/acpi.c;
+  i386_pc = kern/acpi.c;
+  i386_pc = kern/i386/pc/acpi.c;
+
+  enable = efi;
+  enable = i386_pc;
+  enable = i386_coreboot;
+  enable = i386_multiboot;
+};
+
+module = {
+  name = lsacpi;
+
+  common = commands/lsacpi.c;
+
+  enable = efi;
+  enable = i386_pc;
+  enable = i386_coreboot;
+  enable = i386_multiboot;
+};
+
+module = {
+  name = lsefisystab;
+
+  common = commands/efi/lsefisystab.c;
+
+  enable = efi;
+};
+
+module = {
+  name = lssal;
+
+  common = commands/efi/lssal.c;
+
+  enable = efi;
+};
+
+module = {
+  name = lsefimmap;
+
+  common = commands/efi/lsefimmap.c;
+
+  enable = efi;
+};
+
+module = {
+  name = lsefi;
+  common = commands/efi/lsefi.c;
+  enable = efi;
+};
+
+module = {
+  name = efifwsetup;
+  efi = commands/efi/efifwsetup.c;
+  enable = efi;
+};
+
+module = {
+  name = blocklist;
+  common = commands/blocklist.c;
+};
+
+module = {
+  name = boot;
+  common = commands/boot.c;
+  i386_pc = lib/i386/pc/biosnum.c;
+  enable = x86;
+  enable = emu;
+  enable = sparc64_ieee1275;
+  enable = powerpc_ieee1275;
+  enable = mips_arc;
+  enable = ia64_efi;
+  enable = arm_efi;
+  enable = arm64_efi;
+  enable = arm_uboot;
+  enable = arm_coreboot;
+  enable = riscv32_efi;
+  enable = riscv64_efi;
+};
+
+module = {
+  name = cat;
+  common = commands/cat.c;
+};
+
+module = {
+  name = cmp;
+  common = commands/cmp.c;
+};
+
+module = {
+  name = configfile;
+  common = commands/configfile.c;
+};
+
+module = {
+  name = cpuid;
+  common = commands/i386/cpuid.c;
+  enable = x86;
+  enable = i386_xen_pvh;
+  enable = i386_xen;
+  enable = x86_64_xen;
+};
+
+module = {
+  name = date;
+  common = commands/date.c;
+};
+
+module = {
+  name = drivemap;
+
+  i386_pc = commands/i386/pc/drivemap.c;
+  i386_pc = commands/i386/pc/drivemap_int13h.S;
+  enable = i386_pc;
+};
+
+module = {
+  name = echo;
+  common = commands/echo.c;
+};
+
+module = {
+  name = eval;
+  common = commands/eval.c;
+};
+
+module = {
+  name = extcmd;
+  common = commands/extcmd.c;
+  common = lib/arg.c;
+  enable = terminfomodule;
+};
+
+module = {
+  name = fixvideo;
+  common = commands/efi/fixvideo.c;
+  enable = i386_efi;
+  enable = x86_64_efi;
+};
+
+module = {
+  name = gptsync;
+  common = commands/gptsync.c;
+};
+
+module = {
+  name = halt;
+  nopc = commands/halt.c;
+  i386_pc = commands/i386/pc/halt.c;
+  i386_pc = commands/acpihalt.c;
+  i386_coreboot = commands/acpihalt.c;
+  i386_multiboot = commands/acpihalt.c;
+  i386_efi = commands/acpihalt.c;
+  x86_64_efi = commands/acpihalt.c;
+  i386_multiboot = lib/i386/halt.c;
+  i386_coreboot = lib/i386/halt.c;
+  i386_qemu = lib/i386/halt.c;
+  xen = lib/xen/halt.c;
+  i386_xen_pvh = lib/xen/halt.c;
+  efi = lib/efi/halt.c;
+  ieee1275 = lib/ieee1275/halt.c;
+  emu = lib/emu/halt.c;
+  uboot = lib/dummy/halt.c;
+  arm_coreboot = lib/dummy/halt.c;
+};
+
+module = {
+  name = reboot;
+  i386 = lib/i386/reboot.c;
+  i386 = lib/i386/reboot_trampoline.S;
+  powerpc_ieee1275 = lib/ieee1275/reboot.c;
+  sparc64_ieee1275 = lib/ieee1275/reboot.c;
+  mips_arc = lib/mips/arc/reboot.c;
+  mips_loongson = lib/mips/loongson/reboot.c;
+  mips_qemu_mips = lib/mips/qemu_mips/reboot.c;
+  xen = lib/xen/reboot.c;
+  i386_xen_pvh = lib/xen/reboot.c;
+  uboot = lib/uboot/reboot.c;
+  arm_coreboot = lib/dummy/reboot.c;
+  common = commands/reboot.c;
+};
+
+module = {
+  name = hashsum;
+  common = commands/hashsum.c;
+};
+
+module = {
+  name = pgp;
+  common = commands/pgp.c;
+  cflags = '$(CFLAGS_POSIX)';
+  cppflags = '-I$(srcdir)/lib/posix_wrap';
+};
+
+module = {
+  name = verifiers;
+  common = commands/verifiers.c;
+};
+
+module = {
+  name = shim_lock;
+  common = commands/efi/shim_lock.c;
+  enable = x86_64_efi;
+};
+
+module = {
+  name = hdparm;
+  common = commands/hdparm.c;
+  enable = pci;
+  enable = mips_qemu_mips;
+};
+
+module = {
+  name = help;
+  common = commands/help.c;
+};
+
+module = {
+  name = hexdump;
+  common = commands/hexdump.c;
+  common = lib/hexdump.c;
+};
+
+module = {
+  name = keystatus;
+  common = commands/keystatus.c;
+};
+
+module = {
+  name = loadbios;
+  common = commands/efi/loadbios.c;
+  enable = i386_efi;
+  enable = x86_64_efi;
+};
+
+module = {
+  name = loadenv;
+  common = commands/loadenv.c;
+  common = lib/envblk.c;
+};
+
+module = {
+  name = ls;
+  common = commands/ls.c;
+};
+
+module = {
+  name = lsmmap;
+  common = commands/lsmmap.c;
+};
+
+module = {
+  name = lspci;
+  common = commands/lspci.c;
+
+  enable = pci;
+};
+
+module = {
+  name = memrw;
+  common = commands/memrw.c;
+};
+
+module = {
+  name = minicmd;
+  common = commands/minicmd.c;
+};
+
+module = {
+  name = parttool;
+  common = commands/parttool.c;
+};
+
+module = {
+  name = password;
+  common = commands/password.c;
+};
+
+module = {
+  name = password_pbkdf2;
+  common = commands/password_pbkdf2.c;
+};
+
+module = {
+  name = play;
+  x86 = commands/i386/pc/play.c;
+  enable = x86;
+};
+
+module = {
+  name = spkmodem;
+  x86 = term/spkmodem.c;
+  enable = x86;
+};
+
+module = {
+  name = morse;
+  x86 = term/morse.c;
+  enable = x86;
+};
+
+module = {
+  name = probe;
+  common = commands/probe.c;
+};
+
+module = {
+  name = read;
+  common = commands/read.c;
+};
+
+module = {
+  name = search;
+  common = commands/search_wrap.c;
+  extra_dist = commands/search.c;
+};
+
+module = {
+  name = search_fs_file;
+  common = commands/search_file.c;
+};
+
+module = {
+  name = search_fs_uuid;
+  common = commands/search_uuid.c;
+};
+
+module = {
+  name = search_label;
+  common = commands/search_label.c;
+};
+
+module = {
+  name = setpci;
+  common = commands/setpci.c;
+  enable = pci;
+};
+
+module = {
+  name = pcidump;
+  common = commands/pcidump.c;
+  enable = pci;
+};
+
+module = {
+  name = sleep;
+  common = commands/sleep.c;
+};
+
+module = {
+  name = suspend;
+  ieee1275 = commands/ieee1275/suspend.c;
+  enable = i386_ieee1275;
+  enable = powerpc_ieee1275;
+};
+
+module = {
+  name = escc;
+  ieee1275 = term/ieee1275/escc.c;
+  enable = powerpc_ieee1275;
+};
+
+module = {
+  name = terminal;
+  common = commands/terminal.c;
+};
+
+module = {
+  name = test;
+  common = commands/test.c;
+};
+
+module = {
+  name = true;
+  common = commands/true.c;
+};
+
+module = {
+  name = usbtest;
+  common = commands/usbtest.c;
+  enable = usb;
+};
+
+module = {
+  name = videoinfo;
+  common = commands/videoinfo.c;
+};
+
+module = {
+  name = videotest;
+  common = commands/videotest.c;
+};
+
+module = {
+  name = xnu_uuid;
+  common = commands/xnu_uuid.c;
+};
+
+module = {
+  name = dm_nv;
+  common = disk/dmraid_nvidia.c;
+};
+
+module = {
+  name = loopback;
+  common = disk/loopback.c;
+};
+
+module = {
+  name = cryptodisk;
+  common = disk/cryptodisk.c;
+};
+
+module = {
+  name = luks;
+  common = disk/luks.c;
+  common = disk/AFSplitter.c;
+};
+
+module = {
+  name = geli;
+  common = disk/geli.c;
+};
+
+module = {
+  name = lvm;
+  common = disk/lvm.c;
+};
+
+module = {
+  name = ldm;
+  common = disk/ldm.c;
+};
+
+module = {
+  name = mdraid09;
+  common = disk/mdraid_linux.c;
+};
+
+module = {
+  name = mdraid09_be;
+  common = disk/mdraid_linux_be.c;
+};
+
+module = {
+  name = mdraid1x;
+  common = disk/mdraid1x_linux.c;
+};
+
+module = {
+  name = diskfilter;
+  common = disk/diskfilter.c;
+};
+
+module = {
+  name = raid5rec;
+  common = disk/raid5_recover.c;
+};
+
+module = {
+  name = raid6rec;
+  common = disk/raid6_recover.c;
+};
+
+module = {
+  name = scsi;
+  common = disk/scsi.c;
+};
+
+module = {
+  name = memdisk;
+  common = disk/memdisk.c;
+};
+
+module = {
+  name = ata;
+  common = disk/ata.c;
+  enable = pci;
+  enable = mips_qemu_mips;
+};
+
+module = {
+  name = ahci;
+  common = disk/ahci.c;
+  enable = pci;
+};
+
+module = {
+  name = pata;
+  common = disk/pata.c;
+  enable = pci;
+  enable = mips_qemu_mips;
+};
+
+module = {
+  name = biosdisk;
+  i386_pc = disk/i386/pc/biosdisk.c;
+  enable = i386_pc;
+};
+
+module = {
+  name = usbms;
+  common = disk/usbms.c;
+  enable = usb;
+};
+
+module = {
+  name = nand;
+  ieee1275 = disk/ieee1275/nand.c;
+  enable = i386_ieee1275;
+};
+
+module = {
+  name = efiemu;
+  common = efiemu/main.c;
+  common = efiemu/i386/loadcore32.c;
+  common = efiemu/i386/loadcore64.c;
+  i386_pc = efiemu/i386/pc/cfgtables.c;
+  i386_coreboot = efiemu/i386/pc/cfgtables.c;
+  i386_multiboot = efiemu/i386/pc/cfgtables.c;
+  i386_ieee1275 = efiemu/i386/nocfgtables.c;
+  i386_qemu = efiemu/i386/nocfgtables.c;
+  common = efiemu/mm.c;
+  common = efiemu/loadcore_common.c;
+  common = efiemu/symbols.c;
+  common = efiemu/loadcore32.c;
+  common = efiemu/loadcore64.c;
+  common = efiemu/prepare32.c;
+  common = efiemu/prepare64.c;
+  common = efiemu/pnvram.c;
+  common = efiemu/i386/coredetect.c;
+
+  extra_dist = efiemu/prepare.c;
+  extra_dist = efiemu/loadcore.c;
+  extra_dist = efiemu/runtime/efiemu.S;
+  extra_dist = efiemu/runtime/efiemu.c;
+
+  enable = i386_pc;
+  enable = i386_coreboot;
+  enable = i386_ieee1275;
+  enable = i386_multiboot;
+  enable = i386_qemu;
+};
+
+module = {
+  name = font;
+  common = font/font.c;
+  common = font/font_cmd.c;
+  enable = videomodules;
+};
+
+module = {
+  name = procfs;
+  common = fs/proc.c;
+};
+
+module = {
+  name = affs;
+  common = fs/affs.c;
+};
+
+module = {
+  name = afs;
+  common = fs/afs.c;
+};
+
+module = {
+  name = bfs;
+  common = fs/bfs.c;
+};
+
+module = {
+  name = zstd;
+  common = lib/zstd/debug.c;
+  common = lib/zstd/entropy_common.c;
+  common = lib/zstd/error_private.c;
+  common = lib/zstd/fse_decompress.c;
+  common = lib/zstd/huf_decompress.c;
+  common = lib/zstd/module.c;
+  common = lib/zstd/xxhash.c;
+  common = lib/zstd/zstd_common.c;
+  common = lib/zstd/zstd_decompress.c;
+  cflags = '$(CFLAGS_POSIX) -Wno-undef';
+  cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/zstd';
+};
+
+module = {
+  name = btrfs;
+  common = fs/btrfs.c;
+  common = lib/crc.c;
+  cflags = '$(CFLAGS_POSIX) -Wno-undef';
+  cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/minilzo -I$(srcdir)/lib/zstd -DMINILZO_HAVE_CONFIG_H';
+};
+
+module = {
+  name = archelp;
+  common = fs/archelp.c;
+};
+
+module = {
+  name = cbfs;
+  common = fs/cbfs.c;
+};
+
+module = {
+  name = cpio;
+  common = fs/cpio.c;
+};
+
+module = {
+  name = cpio_be;
+  common = fs/cpio_be.c;
+};
+
+module = {
+  name = newc;
+  common = fs/newc.c;
+};
+
+module = {
+  name = odc;
+  common = fs/odc.c;
+};
+
+module = {
+  name = ext2;
+  common = fs/ext2.c;
+};
+
+module = {
+  name = fat;
+  common = fs/fat.c;
+};
+
+module = {
+  name = exfat;
+  common = fs/exfat.c;
+};
+
+module = {
+  name = f2fs;
+  common = fs/f2fs.c;
+};
+
+module = {
+  name = fshelp;
+  common = fs/fshelp.c;
+};
+
+module = {
+  name = hfs;
+  common = fs/hfs.c;
+};
+
+module = {
+  name = hfsplus;
+  common = fs/hfsplus.c;
+};
+
+module = {
+  name = hfspluscomp;
+  common = fs/hfspluscomp.c;
+};
+
+module = {
+  name = iso9660;
+  common = fs/iso9660.c;
+};
+
+module = {
+  name = jfs;
+  common = fs/jfs.c;
+};
+
+module = {
+  name = minix;
+  common = fs/minix.c;
+};
+
+module = {
+  name = minix2;
+  common = fs/minix2.c;
+};
+
+module = {
+  name = minix3;
+  common = fs/minix3.c;
+};
+
+module = {
+  name = minix_be;
+  common = fs/minix_be.c;
+};
+
+module = {
+  name = minix2_be;
+  common = fs/minix2_be.c;
+};
+
+module = {
+  name = minix3_be;
+  common = fs/minix3_be.c;
+};
+
+module = {
+  name = nilfs2;
+  common = fs/nilfs2.c;
+};
+
+module = {
+  name = ntfs;
+  common = fs/ntfs.c;
+};
+
+module = {
+  name = ntfscomp;
+  common = fs/ntfscomp.c;
+};
+
+module = {
+  name = reiserfs;
+  common = fs/reiserfs.c;
+};
+
+module = {
+  name = romfs;
+  common = fs/romfs.c;
+};
+
+module = {
+  name = sfs;
+  common = fs/sfs.c;
+};
+
+module = {
+  name = squash4;
+  common = fs/squash4.c;
+  cflags = '$(CFLAGS_POSIX) -Wno-undef';
+  cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/xzembed -I$(srcdir)/lib/minilzo -DMINILZO_HAVE_CONFIG_H';
+};
+
+module = {
+  name = tar;
+  common = fs/tar.c;
+};
+
+module = {
+  name = udf;
+  common = fs/udf.c;
+};
+
+module = {
+  name = ufs1;
+  common = fs/ufs.c;
+};
+
+module = {
+  name = ufs1_be;
+  common = fs/ufs_be.c;
+};
+
+module = {
+  name = ufs2;
+  common = fs/ufs2.c;
+};
+
+module = {
+  name = xfs;
+  common = fs/xfs.c;
+};
+
+module = {
+  name = zfs;
+  common = fs/zfs/zfs.c;
+  common = fs/zfs/zfs_lzjb.c;
+  common = fs/zfs/zfs_lz4.c;
+  common = fs/zfs/zfs_sha256.c;
+  common = fs/zfs/zfs_fletcher.c;
+};
+
+module = {
+  name = zfscrypt;
+  common = fs/zfs/zfscrypt.c;
+};
+
+module = {
+  name = zfsinfo;
+  common = fs/zfs/zfsinfo.c;
+};
+
+module = {
+  name = macbless;
+  common = commands/macbless.c;
+};
+
+module = {
+  name = pxe;
+  i386_pc = net/drivers/i386/pc/pxe.c;
+  enable = i386_pc;
+};
+
+module = {
+  name = gettext;
+  common = gettext/gettext.c;
+};
+
+module = {
+  name = gfxmenu;
+  common = gfxmenu/gfxmenu.c;
+  common = gfxmenu/view.c;
+  common = gfxmenu/font.c;
+  common = gfxmenu/icon_manager.c;
+  common = gfxmenu/theme_loader.c;
+  common = gfxmenu/widget-box.c;
+  common = gfxmenu/gui_canvas.c;
+  common = gfxmenu/gui_circular_progress.c;
+  common = gfxmenu/gui_box.c;
+  common = gfxmenu/gui_label.c;
+  common = gfxmenu/gui_list.c;
+  common = gfxmenu/gui_image.c;
+  common = gfxmenu/gui_progress_bar.c;
+  common = gfxmenu/gui_util.c;
+  common = gfxmenu/gui_string_util.c;
+};
+
+/* Added by longpanda for Ventoy Project */
+module = {
+  name = ventoy;
+  common = ventoy/ventoy.c;
+  common = ventoy/ventoy_linux.c;
+  common = ventoy/ventoy_windows.c;
+  common = ventoy/ventoy_plugin.c;
+  common = ventoy/ventoy_json.c;
+  common = ventoy/lzx.c;
+  common = ventoy/huffman.c;
+};
+
+module = {
+  name = hello;
+  common = hello/hello.c;
+};
+
+module = {
+  name = gzio;
+  common = io/gzio.c;
+};
+
+module = {
+  name = offsetio;
+  common = io/offset.c;
+};
+
+module = {
+  name = bufio;
+  common = io/bufio.c;
+  enable = videomodules;
+};
+
+module = {
+  name = elf;
+  common = kern/elf.c;
+
+  extra_dist = kern/elfXX.c;
+};
+
+module = {
+  name = crypto;
+  common = lib/crypto.c;
+
+  extra_dist = lib/libgcrypt-grub/cipher/crypto.lst;
+};
+
+module = {
+  name = pbkdf2;
+  common = lib/pbkdf2.c;
+};
+
+module = {
+  name = relocator;
+  common = lib/relocator.c;
+  x86 = lib/i386/relocator16.S;
+  x86 = lib/i386/relocator32.S;
+  x86 = lib/i386/relocator64.S;
+  i386_xen_pvh = lib/i386/relocator16.S;
+  i386_xen_pvh = lib/i386/relocator32.S;
+  i386_xen_pvh = lib/i386/relocator64.S;
+  i386 = lib/i386/relocator_asm.S;
+  i386_xen_pvh = lib/i386/relocator_asm.S;
+  x86_64 = lib/x86_64/relocator_asm.S;
+  i386_xen = lib/i386/relocator_asm.S;
+  x86_64_xen = lib/x86_64/relocator_asm.S;
+  x86 = lib/i386/relocator.c;
+  x86 = lib/i386/relocator_common_c.c;
+  i386_xen_pvh = lib/i386/relocator.c;
+  i386_xen_pvh = lib/i386/relocator_common_c.c;
+  ieee1275 = lib/ieee1275/relocator.c;
+  efi = lib/efi/relocator.c;
+  mips = lib/mips/relocator_asm.S;
+  mips = lib/mips/relocator.c;
+  powerpc = lib/powerpc/relocator_asm.S;
+  powerpc = lib/powerpc/relocator.c;
+  xen = lib/xen/relocator.c;
+  i386_xen = lib/i386/xen/relocator.S;
+  x86_64_xen = lib/x86_64/xen/relocator.S;
+  xen = lib/i386/relocator_common_c.c;
+  x86_64_efi = lib/x86_64/efi/relocator.c;
+
+  extra_dist = lib/i386/relocator_common.S;
+  extra_dist = kern/powerpc/cache_flush.S;
+
+  enable = mips;
+  enable = powerpc;
+  enable = x86;
+  enable = i386_xen_pvh;
+  enable = xen;
+};
+
+module = {
+  name = datetime;
+  cmos = lib/cmos_datetime.c;
+  efi = lib/efi/datetime.c;
+  uboot = lib/dummy/datetime.c;
+  arm_coreboot = lib/dummy/datetime.c;
+  sparc64_ieee1275 = lib/ieee1275/datetime.c;
+  powerpc_ieee1275 = lib/ieee1275/datetime.c;
+  sparc64_ieee1275 = lib/ieee1275/cmos.c;
+  powerpc_ieee1275 = lib/ieee1275/cmos.c;
+  xen = lib/xen/datetime.c;
+  i386_xen_pvh = lib/xen/datetime.c;
+
+  mips_arc = lib/arc/datetime.c;
+  enable = noemu;
+};
+
+module = {
+  name = setjmp;
+  common = lib/setjmp.S;
+  extra_dist = lib/i386/setjmp.S;
+  extra_dist = lib/mips/setjmp.S;
+  extra_dist = lib/x86_64/setjmp.S;
+  extra_dist = lib/sparc64/setjmp.S;
+  extra_dist = lib/powerpc/setjmp.S;
+  extra_dist = lib/ia64/setjmp.S;
+  extra_dist = lib/ia64/longjmp.S;
+  extra_dist = lib/arm/setjmp.S;
+  extra_dist = lib/arm64/setjmp.S;
+  extra_dist = lib/riscv/setjmp.S;
+};
+
+module = {
+  name = aout;
+  common = loader/aout.c;
+  enable = x86;
+};
+
+module = {
+  name = bsd;
+  x86 = loader/i386/bsd.c;
+  x86 = loader/i386/bsd32.c;
+  x86 = loader/i386/bsd64.c;
+
+  extra_dist = loader/i386/bsdXX.c;
+  extra_dist = loader/i386/bsd_pagetable.c;
+
+  enable = x86;
+};
+
+module = {
+  name = plan9;
+  i386_pc = loader/i386/pc/plan9.c;
+  enable = i386_pc;
+};
+
+
+module = {
+  name = linux16;
+  common = loader/i386/pc/linux.c;
+  enable = x86;
+};
+
+module = {
+  name = ntldr;
+  i386_pc = loader/i386/pc/ntldr.c;
+  enable = i386_pc;
+};
+
+
+module = {
+  name = truecrypt;
+  i386_pc = loader/i386/pc/truecrypt.c;
+  enable = i386_pc;
+};
+
+
+module = {
+  name = freedos;
+  i386_pc = loader/i386/pc/freedos.c;
+  enable = i386_pc;
+};
+
+module = {
+  name = pxechain;
+  i386_pc = loader/i386/pc/pxechainloader.c;
+  enable = i386_pc;
+};
+
+module = {
+  name = multiboot2;
+  cppflags = "-DGRUB_USE_MULTIBOOT2";
+
+  common = loader/multiboot.c;
+  common = loader/multiboot_mbi2.c;
+  enable = x86;
+  enable = i386_xen_pvh;
+  enable = mips;
+};
+
+module = {
+  name = multiboot;
+  common = loader/multiboot.c;
+  x86 = loader/i386/multiboot_mbi.c;
+  i386_xen_pvh = loader/i386/multiboot_mbi.c;
+  extra_dist = loader/multiboot_elfxx.c;
+  enable = x86;
+  enable = i386_xen_pvh;
+};
+
+module = {
+  name = xen_boot;
+  arm64 = loader/arm64/xen_boot.c;
+  enable = arm64;
+};
+
+module = {
+  name = linux;
+  x86 = loader/i386/linux.c;
+  i386_xen_pvh = loader/i386/linux.c;
+  xen = loader/i386/xen.c;
+  i386_pc = lib/i386/pc/vesa_modes_table.c;
+  i386_xen_pvh = lib/i386/pc/vesa_modes_table.c;
+  mips = loader/mips/linux.c;
+  powerpc_ieee1275 = loader/powerpc/ieee1275/linux.c;
+  sparc64_ieee1275 = loader/sparc64/ieee1275/linux.c;
+  ia64_efi = loader/ia64/efi/linux.c;
+  arm_coreboot = loader/arm/linux.c;
+  arm_efi = loader/arm64/linux.c;
+  arm_uboot = loader/arm/linux.c;
+  arm64 = loader/arm64/linux.c;
+  riscv32 = loader/riscv/linux.c;
+  riscv64 = loader/riscv/linux.c;
+  common = loader/linux.c;
+  common = lib/cmdline.c;
+  enable = noemu;
+};
+
+module = {
+  name = fdt;
+  efi = loader/efi/fdt.c;
+  common = lib/fdt.c;
+  enable = fdt;
+};
+
+module = {
+  name = xnu;
+  x86 = loader/xnu_resume.c;
+  x86 = loader/i386/xnu.c;
+  x86 = loader/xnu.c;
+
+  /* Code is pretty generic but relies on RNG which
+     is available only on few platforms. It's not a
+     big deal as xnu needs ACPI anyway and we have
+     RNG on all platforms with ACPI.
+   */
+  enable = i386_multiboot;
+  enable = i386_coreboot;
+  enable = i386_pc;
+  enable = i386_efi;
+  enable = x86_64_efi;
+};
+
+module = {
+  name = random;
+  x86 = lib/i386/random.c;
+  common = lib/random.c;
+
+  i386_multiboot = kern/i386/tsc_pmtimer.c;
+  i386_coreboot = kern/i386/tsc_pmtimer.c;
+  i386_pc = kern/i386/tsc_pmtimer.c;
+
+  enable = i386_multiboot;
+  enable = i386_coreboot;
+  enable = i386_pc;
+  enable = i386_efi;
+  enable = x86_64_efi;
+};
+
+module = {
+  name = macho;
+
+  common = loader/macho.c;
+  common = loader/macho32.c;
+  common = loader/macho64.c;
+  common = loader/lzss.c;
+  extra_dist = loader/machoXX.c;
+};
+
+module = {
+  name = appleldr;
+  common = loader/efi/appleloader.c;
+  enable = i386_efi;
+  enable = x86_64_efi;
+};
+
+module = {
+  name = chain;
+  efi = loader/efi/chainloader.c;
+  i386_pc = loader/i386/pc/chainloader.c;
+  i386_coreboot = loader/i386/coreboot/chainloader.c;
+  i386_coreboot = lib/LzmaDec.c;
+  enable = i386_pc;
+  enable = i386_coreboot;
+  enable = efi;
+};
+
+module = {
+  name = mmap;
+  common = mmap/mmap.c;
+  x86 = mmap/i386/uppermem.c;
+  x86 = mmap/i386/mmap.c;
+  i386_xen_pvh = mmap/i386/uppermem.c;
+  i386_xen_pvh = mmap/i386/mmap.c;
+
+  i386_pc = mmap/i386/pc/mmap.c;
+  i386_pc = mmap/i386/pc/mmap_helper.S;
+
+  efi = mmap/efi/mmap.c;
+
+  mips = mmap/mips/uppermem.c;
+
+  enable = x86;
+  enable = i386_xen_pvh;
+  enable = ia64_efi;
+  enable = arm_efi;
+  enable = arm64_efi;
+  enable = riscv32_efi;
+  enable = riscv64_efi;
+  enable = mips;
+};
+
+module = {
+  name = normal;
+  common = normal/main.c;
+  common = normal/cmdline.c;
+  common = normal/dyncmd.c;
+  common = normal/auth.c;
+  common = normal/autofs.c;
+  common = normal/color.c;
+  common = normal/completion.c;
+  common = normal/datetime.c;
+  common = normal/menu.c;
+  common = normal/menu_entry.c;
+  common = normal/menu_text.c;
+  common = normal/misc.c;
+  common = normal/crypto.c;
+  common = normal/term.c;
+  common = normal/context.c;
+  common = normal/charset.c;
+  common = lib/getline.c;
+
+  common = script/main.c;
+  common = script/script.c;
+  common = script/execute.c;
+  common = script/function.c;
+  common = script/lexer.c;
+  common = script/argv.c;
+
+  common = commands/menuentry.c;
+
+  common = unidata.c;
+  common_nodist = grub_script.tab.c;
+  common_nodist = grub_script.yy.c;
+  common_nodist = grub_script.tab.h;
+  common_nodist = grub_script.yy.h;
+
+  extra_dist = script/yylex.l;
+  extra_dist = script/parser.y;
+
+  cflags = '$(CFLAGS_POSIX) -Wno-redundant-decls';
+  cppflags = '$(CPPFLAGS_POSIX)';
+};
+
+module = {
+  name = part_acorn;
+  common = partmap/acorn.c;
+};
+
+module = {
+  name = part_amiga;
+  common = partmap/amiga.c;
+};
+
+module = {
+  name = part_apple;
+  common = partmap/apple.c;
+};
+
+module = {
+  name = part_gpt;
+  common = partmap/gpt.c;
+};
+
+module = {
+  name = part_msdos;
+  common = partmap/msdos.c;
+};
+
+module = {
+  name = part_sun;
+  common = partmap/sun.c;
+};
+
+module = {
+  name = part_plan;
+  common = partmap/plan.c;
+};
+
+module = {
+  name = part_dvh;
+  common = partmap/dvh.c;
+};
+
+module = {
+  name = part_bsd;
+  common = partmap/bsdlabel.c;
+};
+
+module = {
+  name = part_sunpc;
+  common = partmap/sunpc.c;
+};
+
+module = {
+  name = part_dfly;
+  common = partmap/dfly.c;
+};
+
+module = {
+  name = msdospart;
+  common = parttool/msdospart.c;
+};
+
+module = {
+  name = at_keyboard;
+  common = term/at_keyboard.c;
+  common = term/ps2.c;
+  enable = x86;
+};
+
+module = {
+  name = gfxterm;
+  common = term/gfxterm.c;
+  enable = videomodules;
+};
+
+module = {
+  name = gfxterm_background;
+  common = term/gfxterm_background.c;
+};
+
+module = {
+  name = serial;
+  common = term/serial.c;
+  x86 = term/ns8250.c;
+  ieee1275 = term/ieee1275/serial.c;
+  mips_arc = term/arc/serial.c;
+  efi = term/efi/serial.c;
+
+  enable = terminfomodule;
+  enable = ieee1275;
+  enable = mips_arc;
+};
+
+module = {
+  name = sendkey;
+  i386_pc = commands/i386/pc/sendkey.c;
+  enable = i386_pc;
+};
+
+module = {
+  name = terminfo;
+  common = term/terminfo.c;
+  common = term/tparm.c;
+  enable = terminfomodule;
+};
+
+module = {
+  name = usb_keyboard;
+  common = term/usb_keyboard.c;
+  enable = usb;
+};
+
+module = {
+  name = vga;
+  common = video/i386/pc/vga.c;
+  enable = i386_pc;
+};
+
+module = {
+  name = vga_text;
+  common = term/i386/pc/vga_text.c;
+  enable = i386_pc;
+};
+
+module = {
+  name = mda_text;
+  common = term/i386/pc/mda_text.c;
+  enable = i386_pc;
+  enable = i386_coreboot_multiboot_qemu;
+};
+
+module = {
+  name = video_cirrus;
+  x86 = video/cirrus.c;
+  enable = x86;
+};
+
+module = {
+  name = video_bochs;
+  x86 = video/bochs.c;
+  enable = x86;
+};
+
+module = {
+  name = functional_test;
+  common = tests/lib/functional_test.c;
+  common = tests/lib/test.c;
+  common = tests/checksums.h;
+  common = tests/video_checksum.c;
+  common = tests/fake_input.c;
+  common = video/capture.c;
+};
+
+module = {
+  name = exfctest;
+  common = tests/example_functional_test.c;
+};
+
+module = {
+  name = strtoull_test;
+  common = tests/strtoull_test.c;
+};
+
+module = {
+  name = setjmp_test;
+  common = tests/setjmp_test.c;
+};
+
+module = {
+  name = signature_test;
+  common = tests/signature_test.c;
+  common = tests/signatures.h;
+};
+
+module = {
+  name = sleep_test;
+  common = tests/sleep_test.c;
+};
+
+module = {
+  name = xnu_uuid_test;
+  common = tests/xnu_uuid_test.c;
+};
+
+module = {
+  name = pbkdf2_test;
+  common = tests/pbkdf2_test.c;
+};
+
+module = {
+  name = legacy_password_test;
+  common = tests/legacy_password_test.c;
+  enable = i386_pc;
+  enable = i386_xen_pvh;
+  enable = i386_efi;
+  enable = x86_64_efi;
+  enable = emu;
+  enable = xen;
+};
+
+module = {
+  name = div;
+  common = lib/division.c;
+  enable = no_softdiv;
+};
+
+module = {
+  name = div_test;
+  common = tests/div_test.c;
+};
+
+module = {
+  name = mul_test;
+  common = tests/mul_test.c;
+};
+
+module = {
+  name = shift_test;
+  common = tests/shift_test.c;
+};
+
+module = {
+  name = cmp_test;
+  common = tests/cmp_test.c;
+};
+
+module = {
+  name = ctz_test;
+  common = tests/ctz_test.c;
+};
+
+module = {
+  name = bswap_test;
+  common = tests/bswap_test.c;
+};
+
+module = {
+  name = videotest_checksum;
+  common = tests/videotest_checksum.c;
+};
+
+module = {
+  name = gfxterm_menu;
+  common = tests/gfxterm_menu.c;
+};
+
+module = {
+  name = cmdline_cat_test;
+  common = tests/cmdline_cat_test.c;
+};
+
+module = {
+  name = bitmap;
+  common = video/bitmap.c;
+};
+
+module = {
+  name = bitmap_scale;
+  common = video/bitmap_scale.c;
+};
+
+module = {
+  name = efi_gop;
+  efi = video/efi_gop.c;
+  enable = efi;
+};
+
+module = {
+  name = efi_uga;
+  efi = video/efi_uga.c;
+  enable = i386_efi;
+  enable = x86_64_efi;
+};
+
+module = {
+  name = jpeg;
+  common = video/readers/jpeg.c;
+};
+
+module = {
+  name = png;
+  common = video/readers/png.c;
+};
+
+module = {
+  name = tga;
+  common = video/readers/tga.c;
+};
+
+module = {
+  name = vbe;
+  common = video/i386/pc/vbe.c;
+  enable = i386_pc;
+};
+
+module = {
+  name = video_fb;
+  common = video/fb/video_fb.c;
+  common = video/fb/fbblit.c;
+  common = video/fb/fbfill.c;
+  common = video/fb/fbutil.c;
+  enable = videomodules;
+};
+
+module = {
+  name = video;
+  common = video/video.c;
+  enable = videomodules;
+};
+
+module = {
+  name = video_colors;
+  common = video/colors.c;
+};
+
+module = {
+  name = ieee1275_fb;
+  ieee1275 = video/ieee1275.c;
+  enable = powerpc_ieee1275;
+};
+
+module = {
+  name = sdl;
+  emu = video/emu/sdl.c;
+  enable = emu;
+  condition = COND_GRUB_EMU_SDL;
+};
+
+module = {
+  name = datehook;
+  common = hook/datehook.c;
+};
+
+module = {
+  name = net;
+  common = net/net.c;
+  common = net/dns.c;
+  common = net/bootp.c;
+  common = net/ip.c;
+  common = net/udp.c;
+  common = net/tcp.c;
+  common = net/icmp.c;
+  common = net/icmp6.c;
+  common = net/ethernet.c;
+  common = net/arp.c;
+  common = net/netbuff.c;
+};
+
+module = {
+  name = tftp;
+  common = net/tftp.c;
+};
+
+module = {
+  name = http;
+  common = net/http.c;
+};
+
+module = {
+  name = ofnet;
+  common = net/drivers/ieee1275/ofnet.c;
+  enable = ieee1275;
+};
+
+module = {
+  name = ubootnet;
+  common = net/drivers/uboot/ubootnet.c;
+  enable = uboot;
+};
+
+module = {
+  name = efinet;
+  common = net/drivers/efi/efinet.c;
+  enable = efi;
+};
+
+module = {
+  name = emunet;
+  emu = net/drivers/emu/emunet.c;
+  enable = emu;
+};
+
+module = {
+  name = legacycfg;
+  common = commands/legacycfg.c;
+  common = lib/legacy_parse.c;
+  emu = lib/i386/pc/vesa_modes_table.c;
+  i386_efi = lib/i386/pc/vesa_modes_table.c;
+  x86_64_efi = lib/i386/pc/vesa_modes_table.c;
+  xen = lib/i386/pc/vesa_modes_table.c;
+
+  enable = i386_pc;
+  enable = i386_xen_pvh;
+  enable = i386_efi;
+  enable = x86_64_efi;
+  enable = emu;
+  enable = xen;
+};
+
+module = {
+  name = syslinuxcfg;
+  common = lib/syslinux_parse.c;
+  common = commands/syslinuxcfg.c;
+};
+
+module = {
+  name = test_blockarg;
+  common = tests/test_blockarg.c;
+};
+
+module = {
+  name = xzio;
+  common = io/xzio.c;
+  common = lib/xzembed/xz_dec_bcj.c;
+  common = lib/xzembed/xz_dec_lzma2.c;
+  common = lib/xzembed/xz_dec_stream.c;
+  cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/xzembed';
+  cflags='-Wno-unreachable-code';
+};
+
+module = {
+  name = lzopio;
+  common = io/lzopio.c;
+  common = lib/minilzo/minilzo.c;
+  cflags = '$(CFLAGS_POSIX) -Wno-undef -Wno-redundant-decls -Wno-error';
+  cppflags = '-I$(srcdir)/lib/posix_wrap -I$(srcdir)/lib/minilzo -DMINILZO_HAVE_CONFIG_H';
+};
+
+module = {
+  name = testload;
+  common = commands/testload.c;
+};
+
+module = {
+  name = backtrace;
+  x86 = lib/i386/backtrace.c;
+  i386_xen_pvh = lib/i386/backtrace.c;
+  i386_xen = lib/i386/backtrace.c;
+  x86_64_xen = lib/i386/backtrace.c;
+  common = lib/backtrace.c;
+  enable = x86;
+  enable = i386_xen_pvh;
+  enable = i386_xen;
+  enable = x86_64_xen;
+};
+
+module = {
+  name = lsapm;
+  common = commands/i386/pc/lsapm.c;
+  enable = i386_pc;
+};
+
+module = {
+  name = keylayouts;
+  common = commands/keylayouts.c;
+  enable = x86;
+};
+
+module = {
+  name = priority_queue;
+  common = lib/priority_queue.c;
+};
+
+module = {
+  name = time;
+  common = commands/time.c;
+};
+
+module = {
+  name = cacheinfo;
+  common = commands/cacheinfo.c;
+  condition = COND_ENABLE_CACHE_STATS;
+};
+
+module = {
+  name = boottime;
+  common = commands/boottime.c;
+  condition = COND_ENABLE_BOOT_TIME_STATS;
+};
+
+module = {
+  name = adler32;
+  common = lib/adler32.c;
+};
+
+module = {
+  name = crc64;
+  common = lib/crc64.c;
+};
+
+module = {
+  name = mpi;
+  common = lib/libgcrypt-grub/mpi/mpiutil.c;
+  common = lib/libgcrypt-grub/mpi/mpi-bit.c;
+  common = lib/libgcrypt-grub/mpi/mpi-add.c;
+  common = lib/libgcrypt-grub/mpi/mpi-mul.c;
+  common = lib/libgcrypt-grub/mpi/mpi-mod.c;
+  common = lib/libgcrypt-grub/mpi/mpi-gcd.c;
+  common = lib/libgcrypt-grub/mpi/mpi-div.c;
+  common = lib/libgcrypt-grub/mpi/mpi-cmp.c;
+  common = lib/libgcrypt-grub/mpi/mpi-inv.c;
+  common = lib/libgcrypt-grub/mpi/mpi-pow.c;
+  common = lib/libgcrypt-grub/mpi/mpi-mpow.c;
+  common = lib/libgcrypt-grub/mpi/mpih-lshift.c;
+  common = lib/libgcrypt-grub/mpi/mpih-mul.c;
+  common = lib/libgcrypt-grub/mpi/mpih-mul1.c;
+  common = lib/libgcrypt-grub/mpi/mpih-mul2.c;
+  common = lib/libgcrypt-grub/mpi/mpih-mul3.c;
+  common = lib/libgcrypt-grub/mpi/mpih-add1.c;
+  common = lib/libgcrypt-grub/mpi/mpih-sub1.c;
+  common = lib/libgcrypt-grub/mpi/mpih-div.c;
+  common = lib/libgcrypt-grub/mpi/mpicoder.c;
+  common = lib/libgcrypt-grub/mpi/mpih-rshift.c;
+  common = lib/libgcrypt-grub/mpi/mpi-inline.c;
+  common = lib/libgcrypt_wrap/mem.c;
+
+  cflags = '$(CFLAGS_GCRY) -Wno-redundant-decls -Wno-sign-compare';
+  cppflags = '$(CPPFLAGS_GCRY)';
+};
+
+module = {
+  name = all_video;
+  common = lib/fake_module.c;
+};
+
+module = {
+  name = gdb;
+  common = gdb/cstub.c;
+  common = gdb/gdb.c;
+  i386 = gdb/i386/idt.c;
+  i386 = gdb/i386/machdep.S;
+  i386 = gdb/i386/signal.c;
+  enable = i386;
+};
+
+module = {
+  name = testspeed;
+  common = commands/testspeed.c;
+};
+
+module = {
+  name = tpm;
+  common = commands/tpm.c;
+  efi = commands/efi/tpm.c;
+  enable = x86_64_efi;
+};
+
+module = {
+  name = tr;
+  common = commands/tr.c;
+};
+
+module = {
+  name = progress;
+  common = lib/progress.c;
+};
+
+module = {
+  name = file;
+  common = commands/file.c;
+  common = commands/file32.c;
+  common = commands/file64.c;
+  extra_dist = commands/fileXX.c;
+  common = loader/i386/xen_file.c;
+  common = loader/i386/xen_file32.c;
+  common = loader/i386/xen_file64.c;
+  extra_dist = loader/i386/xen_fileXX.c;
+};
+module = {
+  name = rdmsr;
+  common = commands/i386/rdmsr.c;
+  enable = x86;
+};
+module = {
+  name = wrmsr;
+  common = commands/i386/wrmsr.c;
+  enable = x86;
+};
diff --git a/GRUB2/grub-2.04/grub-core/boot/i386/pc/boot.S b/GRUB2/grub-2.04/grub-core/boot/i386/pc/boot.S
new file mode 100644 (file)
index 0000000..18c99b3
--- /dev/null
@@ -0,0 +1,545 @@
+/* -*-Asm-*- */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,2000,2001,2002,2005,2006,2007,2008,2009  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/symbol.h>
+#include <grub/machine/boot.h>
+
+/*
+ *  defines for the code go here
+ */
+
+       /* Print message string */
+#define MSG(x) movw $x, %si; call LOCAL(message)
+#define ERR(x) movw $x, %si; jmp LOCAL(error_message)
+
+       .macro floppy
+part_start:
+
+LOCAL(probe_values):
+       .byte   36, 18, 15, 9, 0
+
+LOCAL(floppy_probe):
+       pushw   %dx
+/*
+ *  Perform floppy probe.
+ */
+#ifdef __APPLE__
+       LOCAL(probe_values_minus_one) = LOCAL(probe_values) - 1
+       movw    MACRO_DOLLAR(LOCAL(probe_values_minus_one)), %si
+#else
+       movw    MACRO_DOLLAR(LOCAL(probe_values)) - 1, %si
+#endif
+
+LOCAL(probe_loop):
+       /* reset floppy controller INT 13h AH=0 */
+       xorw    %ax, %ax
+       int     MACRO_DOLLAR(0x13)
+
+       incw    %si
+       movb    (%si), %cl
+
+       /* if number of sectors is 0, display error and die */
+       testb   %cl, %cl
+       jnz     1f
+
+/*
+ * Floppy disk probe failure.
+ */
+       MSG(fd_probe_error_string)
+       jmp     LOCAL(general_error)
+
+/* "Floppy" */
+fd_probe_error_string: .asciz "Floppy"
+
+1:
+       /* perform read */
+       movw    MACRO_DOLLAR(GRUB_BOOT_MACHINE_BUFFER_SEG), %bx
+       movw    %bx, %es
+       xorw    %bx, %bx
+       movw    MACRO_DOLLAR(0x201), %ax
+       movb    MACRO_DOLLAR(0), %ch
+       movb    MACRO_DOLLAR(0), %dh
+       int     MACRO_DOLLAR(0x13)
+
+       /* if error, jump to "LOCAL(probe_loop)" */
+       jc      LOCAL(probe_loop)
+
+       /* %cl is already the correct value! */
+       movb    MACRO_DOLLAR(1), %dh
+       movb    MACRO_DOLLAR(79), %ch
+
+       jmp     LOCAL(final_init)
+       .endm
+
+       .macro scratch
+
+       /* scratch space */
+mode:
+       .byte   0
+disk_address_packet:
+sectors:
+       .long   0
+heads:
+       .long   0
+cylinders:
+       .word   0
+sector_start:
+       .byte   0
+head_start:
+       .byte   0
+cylinder_start:
+       .word   0
+       /* more space... */
+       .endm
+
+       .file   "boot.S"
+
+       .text
+
+       /* Tell GAS to generate 16-bit instructions so that this code works
+          in real mode. */
+       .code16
+
+.globl _start, start;
+_start:
+start:
+       /*
+        * _start is loaded at 0x7c00 and is jumped to with CS:IP 0:0x7c00
+        */
+
+       /*
+        * Beginning of the sector is compatible with the FAT/HPFS BIOS
+        * parameter block.
+        */
+
+       jmp     LOCAL(after_BPB)
+       nop     /* do I care about this ??? */
+
+#ifdef HYBRID_BOOT
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+       nop
+
+       nop
+       nop
+       jmp     LOCAL(after_BPB)
+#else
+       /*
+        * This space is for the BIOS parameter block!!!!  Don't change
+        * the first jump, nor start the code anywhere but right after
+        * this area.
+        */
+
+       .org GRUB_BOOT_MACHINE_BPB_START
+       .org 4
+#endif
+#ifdef HYBRID_BOOT
+       floppy
+#else
+       scratch
+#endif
+
+       .org GRUB_BOOT_MACHINE_BPB_END
+       /*
+        * End of BIOS parameter block.
+        */
+
+LOCAL(kernel_address):
+       .word   GRUB_BOOT_MACHINE_KERNEL_ADDR
+
+#ifndef HYBRID_BOOT
+       .org GRUB_BOOT_MACHINE_KERNEL_SECTOR
+LOCAL(kernel_sector):
+       .long   1
+LOCAL(kernel_sector_high):
+       .long   0
+#endif
+
+       .org GRUB_BOOT_MACHINE_BOOT_DRIVE
+boot_drive:
+       .byte 0xff      /* the disk to load kernel from */
+                       /* 0xff means use the boot drive */
+
+LOCAL(after_BPB):
+
+/* general setup */
+       cli             /* we're not safe here! */
+
+        /*
+         * This is a workaround for buggy BIOSes which don't pass boot
+         * drive correctly. If GRUB is installed into a HDD, check if
+         * DL is masked correctly. If not, assume that the BIOS passed
+         * a bogus value and set DL to 0x80, since this is the only
+         * possible boot drive. If GRUB is installed into a floppy,
+         * this does nothing (only jump).
+         */
+       .org GRUB_BOOT_MACHINE_DRIVE_CHECK
+boot_drive_check:
+        jmp     3f     /* grub-setup may overwrite this jump */
+        testb   $0x80, %dl
+        jz      2f
+3:
+       /* Ignore %dl different from 0-0x0f and 0x80-0x8f.  */
+       testb   $0x70, %dl
+       jz      1f
+2:     
+        movb    $0x80, %dl
+1:
+       /*
+        * ljmp to the next instruction because some bogus BIOSes
+        * jump to 07C0:0000 instead of 0000:7C00.
+        */
+       ljmp    $0, $real_start
+
+real_start:
+
+       /* set up %ds and %ss as offset from 0 */
+       xorw    %ax, %ax
+       movw    %ax, %ds
+       movw    %ax, %ss
+
+       /* set up the REAL stack */
+       movw    $GRUB_BOOT_MACHINE_STACK_SEG, %sp
+
+       sti             /* we're safe again */
+
+       /*
+        *  Check if we have a forced disk reference here
+        */
+       movb   boot_drive, %al
+       cmpb    $0xff, %al
+       je      1f
+       movb    %al, %dl
+1:
+       /* save drive reference first thing! */
+       pushw   %dx
+
+       /* print a notification message on the screen */
+       MSG(notification_string)
+
+       /* set %si to the disk address packet */
+       movw    $disk_address_packet, %si
+
+       /* check if LBA is supported */
+       movb    $0x41, %ah
+       movw    $0x55aa, %bx
+       int     $0x13
+
+       /*
+        *  %dl may have been clobbered by INT 13, AH=41H.
+        *  This happens, for example, with AST BIOS 1.04.
+        */
+       popw    %dx
+       pushw   %dx
+
+       /* use CHS if fails */
+       jc      LOCAL(chs_mode)
+       cmpw    $0xaa55, %bx
+       jne     LOCAL(chs_mode)
+
+       andw    $1, %cx
+       jz      LOCAL(chs_mode)
+
+LOCAL(lba_mode):
+       xorw    %ax, %ax
+       movw    %ax, 4(%si)
+
+       incw    %ax
+       /* set the mode to non-zero */
+       movb    %al, -1(%si)
+
+       /* the blocks */
+       movw    %ax, 2(%si)
+
+       /* the size and the reserved byte */
+       movw    $0x0010, (%si)
+
+       /* the absolute address */
+       movl    LOCAL(kernel_sector), %ebx
+       movl    %ebx, 8(%si)
+       movl    LOCAL(kernel_sector_high), %ebx
+       movl    %ebx, 12(%si)
+
+       /* the segment of buffer address */
+       movw    $GRUB_BOOT_MACHINE_BUFFER_SEG, 6(%si)
+
+/*
+ * BIOS call "INT 0x13 Function 0x42" to read sectors from disk into memory
+ *     Call with       %ah = 0x42
+ *                     %dl = drive number
+ *                     %ds:%si = segment:offset of disk address packet
+ *     Return:
+ *                     %al = 0x0 on success; err code on failure
+ */
+
+       movb    $0x42, %ah
+       int     $0x13
+
+       /* LBA read is not supported, so fallback to CHS.  */
+       jc      LOCAL(chs_mode)
+
+       movw    $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
+       jmp     LOCAL(copy_buffer)
+
+LOCAL(chs_mode):
+       /*
+        *  Determine the hard disk geometry from the BIOS!
+        *  We do this first, so that LS-120 IDE floppies work correctly.
+        */
+       movb    $8, %ah
+       int     $0x13
+       jnc     LOCAL(final_init)
+
+       popw    %dx
+       /*
+        *  The call failed, so maybe use the floppy probe instead.
+        */
+       testb   %dl, %dl
+       jnb     LOCAL(floppy_probe)
+
+       /* Nope, we definitely have a hard disk, and we're screwed. */
+       ERR(hd_probe_error_string)
+
+LOCAL(final_init):
+       /* set the mode to zero */
+       movzbl  %dh, %eax
+       movb    %ah, -1(%si)
+
+       /* save number of heads */
+       incw    %ax
+       movl    %eax, 4(%si)
+
+       movzbw  %cl, %dx
+       shlw    $2, %dx
+       movb    %ch, %al
+       movb    %dh, %ah
+
+       /* save number of cylinders */
+       incw    %ax
+       movw    %ax, 8(%si)
+
+       movzbw  %dl, %ax
+       shrb    $2, %al
+
+       /* save number of sectors */
+       movl    %eax, (%si)
+
+setup_sectors:
+       /* load logical sector start (top half) */
+       movl    LOCAL(kernel_sector_high), %eax
+
+       orl     %eax, %eax
+       jnz     LOCAL(geometry_error)
+
+       /* load logical sector start (bottom half) */
+       movl    LOCAL(kernel_sector), %eax
+
+       /* zero %edx */
+       xorl    %edx, %edx
+
+       /* divide by number of sectors */
+       divl    (%si)
+
+       /* save sector start */
+       movb    %dl, %cl
+
+       xorw    %dx, %dx        /* zero %edx */
+       divl    4(%si)          /* divide by number of heads */
+
+       /* do we need too many cylinders? */
+       cmpw    8(%si), %ax
+       jge     LOCAL(geometry_error)
+
+       /* normalize sector start (1-based) */
+       incb    %cl
+
+       /* low bits of cylinder start */
+       movb    %al, %ch
+
+       /* high bits of cylinder start */
+       xorb    %al, %al
+       shrw    $2, %ax
+       orb     %al, %cl
+
+       /* save head start */
+       movb    %dl, %al
+
+       /* restore %dl */
+       popw    %dx
+
+       /* head start */
+       movb    %al, %dh
+
+/*
+ * BIOS call "INT 0x13 Function 0x2" to read sectors from disk into memory
+ *     Call with       %ah = 0x2
+ *                     %al = number of sectors
+ *                     %ch = cylinder
+ *                     %cl = sector (bits 6-7 are high bits of "cylinder")
+ *                     %dh = head
+ *                     %dl = drive (0x80 for hard disk, 0x0 for floppy disk)
+ *                     %es:%bx = segment:offset of buffer
+ *     Return:
+ *                     %al = 0x0 on success; err code on failure
+ */
+
+       movw    $GRUB_BOOT_MACHINE_BUFFER_SEG, %bx
+       movw    %bx, %es        /* load %es segment with disk buffer */
+
+       xorw    %bx, %bx        /* %bx = 0, put it at 0 in the segment */
+       movw    $0x0201, %ax    /* function 2 */
+       int     $0x13
+
+       jc      LOCAL(read_error)
+
+       movw    %es, %bx
+
+LOCAL(copy_buffer):
+       /*
+        * We need to save %cx and %si because the startup code in
+        * kernel uses them without initializing them.
+        */
+       pusha
+       pushw   %ds
+
+       movw    $0x100, %cx
+       movw    %bx, %ds
+       xorw    %si, %si
+       movw    $GRUB_BOOT_MACHINE_KERNEL_ADDR, %di
+       movw    %si, %es
+
+       cld
+
+       rep
+       movsw
+
+       popw    %ds
+       popa
+
+       /* boot kernel */
+       jmp     *(LOCAL(kernel_address))
+
+/* END OF MAIN LOOP */
+
+/*
+ * BIOS Geometry translation error (past the end of the disk geometry!).
+ */
+LOCAL(geometry_error):
+       ERR(geometry_error_string)
+
+/*
+ * Read error on the disk.
+ */
+LOCAL(read_error):
+       movw    $read_error_string, %si
+LOCAL(error_message):
+       call    LOCAL(message)
+LOCAL(general_error):
+       MSG(general_error_string)
+
+/* go here when you need to stop the machine hard after an error condition */
+        /* tell the BIOS a boot failure, which may result in no effect */
+        int    $0x18
+LOCAL(stop):
+       jmp     LOCAL(stop)
+
+ventoy_uuid:   .ascii  "XXXXXXXXXXXXXXXX"
+notification_string:   .asciz "GR"
+geometry_error_string: .asciz "Ge"
+hd_probe_error_string: .asciz "HD"
+read_error_string:     .asciz "Rd"
+general_error_string:  .asciz " Er\r\n"
+
+
+
+/*
+ * message: write the string pointed to by %si
+ *
+ *   WARNING: trashes %si, %ax, and %bx
+ */
+
+       /*
+        * Use BIOS "int 10H Function 0Eh" to write character in teletype mode
+        *      %ah = 0xe       %al = character
+        *      %bh = page      %bl = foreground color (graphics modes)
+        */
+1:
+       movw    $0x0001, %bx
+       movb    $0xe, %ah
+       int     $0x10           /* display a byte */
+LOCAL(message):
+       lodsb
+       cmpb    $0, %al
+       jne     1b      /* if not end of string, jmp to display */
+       ret
+
+       /*
+        *  Windows NT breaks compatibility by embedding a magic
+        *  number here.
+        */
+
+#ifdef HYBRID_BOOT
+       .org 0x1b0
+LOCAL(kernel_sector):
+       .long   1
+LOCAL(kernel_sector_high):
+       .long   0
+#endif
+       .org GRUB_BOOT_MACHINE_WINDOWS_NT_MAGIC
+nt_magic:
+       .long 0
+       .word 0
+
+       /*
+        *  This is where an MBR would go if on a hard disk.  The code
+        *  here isn't even referenced unless we're on a floppy.  Kinda
+        *  sneaky, huh?
+       */
+
+       .org GRUB_BOOT_MACHINE_PART_START
+
+#ifndef HYBRID_BOOT
+       floppy
+#else
+       scratch
+#endif
+
+       .org GRUB_BOOT_MACHINE_PART_END
+       
+/* the last 2 bytes in the sector 0 contain the signature */
+       .word   GRUB_BOOT_MACHINE_SIGNATURE
diff --git a/GRUB2/grub-2.04/grub-core/commands/blocklist.c b/GRUB2/grub-2.04/grub-core/commands/blocklist.c
new file mode 100644 (file)
index 0000000..6256f37
--- /dev/null
@@ -0,0 +1,162 @@
+/* blocklist.c - print the block list of a file */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2006,2007  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/dl.h>
+#include <grub/misc.h>
+#include <grub/file.h>
+#include <grub/mm.h>
+#include <grub/disk.h>
+#include <grub/partition.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+/* Context for grub_cmd_blocklist.  */
+struct blocklist_ctx
+{
+  unsigned long start_sector;
+  unsigned num_sectors;
+  int num_entries;
+  grub_disk_addr_t part_start;
+};
+
+/* Helper for grub_cmd_blocklist.  */
+static void
+print_blocklist (grub_disk_addr_t sector, unsigned num,
+                unsigned offset, unsigned length, struct blocklist_ctx *ctx)
+{
+  if (ctx->num_entries++)
+    grub_printf (",");
+
+  grub_printf ("%llu", (unsigned long long) (sector - ctx->part_start));
+  if (num > 0)
+    grub_printf ("+%u", num);
+  if (offset != 0 || length != 0)
+    grub_printf ("[%u-%u]", offset, offset + length);
+}
+
+/* Helper for grub_cmd_blocklist.  */
+static void
+read_blocklist (grub_disk_addr_t sector, unsigned offset, unsigned length,
+               void *data)
+{
+  struct blocklist_ctx *ctx = data;
+
+  if (ctx->num_sectors > 0)
+    {
+      if (ctx->start_sector + ctx->num_sectors == sector
+         && offset == 0 && length >= GRUB_DISK_SECTOR_SIZE)
+       {
+         ctx->num_sectors += length >> GRUB_DISK_SECTOR_BITS;
+         sector += length >> GRUB_DISK_SECTOR_BITS;
+         length &= (GRUB_DISK_SECTOR_SIZE - 1);
+       }
+
+      if (!length)
+       return;
+      print_blocklist (ctx->start_sector, ctx->num_sectors, 0, 0, ctx);
+      ctx->num_sectors = 0;
+    }
+
+  if (offset)
+    {
+      unsigned l = length + offset;
+      l &= (GRUB_DISK_SECTOR_SIZE - 1);
+      l -= offset;
+      print_blocklist (sector, 0, offset, l, ctx);
+      length -= l;
+      sector++;
+      offset = 0;
+    }
+
+  if (!length)
+    return;
+
+  if (length & (GRUB_DISK_SECTOR_SIZE - 1))
+    {
+      if (length >> GRUB_DISK_SECTOR_BITS)
+       {
+         print_blocklist (sector, length >> GRUB_DISK_SECTOR_BITS, 0, 0, ctx);
+         sector += length >> GRUB_DISK_SECTOR_BITS;
+       }
+      print_blocklist (sector, 0, 0, length & (GRUB_DISK_SECTOR_SIZE - 1), ctx);
+    }
+  else
+    {
+      ctx->start_sector = sector;
+      ctx->num_sectors = length >> GRUB_DISK_SECTOR_BITS;
+    }
+}
+
+static grub_err_t
+grub_cmd_blocklist (grub_command_t cmd __attribute__ ((unused)),
+                   int argc, char **args)
+{
+  grub_file_t file;
+  char buf[GRUB_DISK_SECTOR_SIZE];
+  struct blocklist_ctx ctx = {
+    .start_sector = 0,
+    .num_sectors = 0,
+    .num_entries = 0,
+    .part_start = 0
+  };
+
+  if (argc < 1)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+
+  file = grub_file_open (args[0], GRUB_FILE_TYPE_PRINT_BLOCKLIST
+                        | GRUB_FILE_TYPE_NO_DECOMPRESS);
+  if (! file)
+    return grub_errno;
+
+  if (! file->device->disk)
+    return grub_error (GRUB_ERR_BAD_DEVICE,
+                      "this command is available only for disk devices");
+
+  ctx.part_start = grub_partition_get_start (file->device->disk->partition);
+
+  file->read_hook = read_blocklist;
+  file->read_hook_data = &ctx;
+
+  while (grub_file_read (file, buf, sizeof (buf)) > 0)
+    ;
+
+  if (ctx.num_sectors > 0)
+    print_blocklist (ctx.start_sector, ctx.num_sectors, 0, 0, &ctx);
+
+  grub_printf("\nentry number:%d \n", ctx.num_entries);
+
+  grub_file_close (file);
+
+  return grub_errno;
+}
+
+static grub_command_t cmd;
+\f
+GRUB_MOD_INIT(blocklist)
+{
+  cmd = grub_register_command ("blocklist", grub_cmd_blocklist,
+                              N_("FILE"), N_("Print a block list."));
+}
+
+GRUB_MOD_FINI(blocklist)
+{
+  grub_unregister_command (cmd);
+}
diff --git a/GRUB2/grub-2.04/grub-core/disk/i386/pc/biosdisk.c b/GRUB2/grub-2.04/grub-core/disk/i386/pc/biosdisk.c
new file mode 100644 (file)
index 0000000..ee2ebe9
--- /dev/null
@@ -0,0 +1,764 @@
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 1999,2000,2001,2002,2003,2004,2005,2006,2007,2008,2009,2010  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/machine/biosdisk.h>
+#include <grub/machine/kernel.h>
+#include <grub/machine/memory.h>
+#include <grub/machine/int.h>
+#include <grub/disk.h>
+#include <grub/dl.h>
+#include <grub/mm.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/err.h>
+#include <grub/term.h>
+#include <grub/i18n.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static int cd_drive = 0;
+static int grub_biosdisk_rw_int13_extensions (int ah, int drive, void *dap);
+
+static int grub_biosdisk_get_num_floppies (void)
+{
+  struct grub_bios_int_registers regs;
+  int drive;
+
+  /* reset the disk system first */
+  regs.eax = 0;
+  regs.edx = 0;
+  regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+
+  grub_bios_interrupt (0x13, &regs);
+
+  for (drive = 0; drive < 2; drive++)
+    {
+      regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT | GRUB_CPU_INT_FLAGS_CARRY;
+      regs.edx = drive;
+
+      /* call GET DISK TYPE */
+      regs.eax = 0x1500;
+      grub_bios_interrupt (0x13, &regs);
+      if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY)
+       break;
+
+      /* check if this drive exists */
+      if (!(regs.eax & 0x300))
+       break;
+    }
+
+  return drive;
+}
+
+/*
+ *   Call IBM/MS INT13 Extensions (int 13 %ah=AH) for DRIVE. DAP
+ *   is passed for disk address packet. If an error occurs, return
+ *   non-zero, otherwise zero.
+ */
+
+static int 
+grub_biosdisk_rw_int13_extensions (int ah, int drive, void *dap)
+{
+  struct grub_bios_int_registers regs;
+  regs.eax = ah << 8;
+  /* compute the address of disk_address_packet */
+  regs.ds = (((grub_addr_t) dap) & 0xffff0000) >> 4;
+  regs.esi = (((grub_addr_t) dap) & 0xffff);
+  regs.edx = drive;
+  regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+
+  grub_bios_interrupt (0x13, &regs);
+  return (regs.eax >> 8) & 0xff;
+}
+
+/*
+ *   Call standard and old INT13 (int 13 %ah=AH) for DRIVE. Read/write
+ *   NSEC sectors from COFF/HOFF/SOFF into SEGMENT. If an error occurs,
+ *   return non-zero, otherwise zero.
+ */
+static int 
+grub_biosdisk_rw_standard (int ah, int drive, int coff, int hoff,
+                          int soff, int nsec, int segment)
+{
+  int ret, i;
+
+  /* Try 3 times.  */
+  for (i = 0; i < 3; i++)
+    {
+      struct grub_bios_int_registers regs;
+
+      /* set up CHS information */
+      /* set %ch to low eight bits of cylinder */
+      regs.ecx = (coff << 8) & 0xff00;
+      /* set bits 6-7 of %cl to high two bits of cylinder */
+      regs.ecx |= (coff >> 2) & 0xc0;
+      /* set bits 0-5 of %cl to sector */
+      regs.ecx |= soff & 0x3f;
+
+      /* set %dh to head and %dl to drive */  
+      regs.edx = (drive & 0xff) | ((hoff << 8) & 0xff00);
+      /* set %ah to AH */
+      regs.eax = (ah << 8) & 0xff00;
+      /* set %al to NSEC */
+      regs.eax |= nsec & 0xff;
+
+      regs.ebx = 0;
+      regs.es = segment;
+
+      regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+
+      grub_bios_interrupt (0x13, &regs);
+      /* check if successful */
+      if (!(regs.flags & GRUB_CPU_INT_FLAGS_CARRY))
+       return 0;
+
+      /* save return value */
+      ret = regs.eax >> 8;
+
+      /* if fail, reset the disk system */
+      regs.eax = 0;
+      regs.edx = (drive & 0xff);
+      regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+      grub_bios_interrupt (0x13, &regs);
+    }
+  return ret;
+}
+
+/*
+ *   Check if LBA is supported for DRIVE. If it is supported, then return
+ *   the major version of extensions, otherwise zero.
+ */
+static int
+grub_biosdisk_check_int13_extensions (int drive)
+{
+  struct grub_bios_int_registers regs;
+
+  regs.edx = drive & 0xff;
+  regs.eax = 0x4100;
+  regs.ebx = 0x55aa;
+  regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+  grub_bios_interrupt (0x13, &regs);
+  
+  if (regs.flags & GRUB_CPU_INT_FLAGS_CARRY)
+    return 0;
+
+  if ((regs.ebx & 0xffff) != 0xaa55)
+    return 0;
+
+  /* check if AH=0x42 is supported */
+  if (!(regs.ecx & 1))
+    return 0;
+
+  return (regs.eax >> 8) & 0xff;
+}
+
+/*
+ *   Return the geometry of DRIVE in CYLINDERS, HEADS and SECTORS. If an
+ *   error occurs, then return non-zero, otherwise zero.
+ */
+static int 
+grub_biosdisk_get_diskinfo_standard (int drive,
+                                    unsigned long *cylinders,
+                                    unsigned long *heads,
+                                    unsigned long *sectors)
+{
+  struct grub_bios_int_registers regs;
+
+  regs.eax = 0x0800;
+  regs.edx = drive & 0xff;
+
+  regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+  grub_bios_interrupt (0x13, &regs);
+
+  /* Check if unsuccessful. Ignore return value if carry isn't set to 
+     workaround some buggy BIOSes. */
+  if ((regs.flags & GRUB_CPU_INT_FLAGS_CARRY) && ((regs.eax & 0xff00) != 0))
+    return (regs.eax & 0xff00) >> 8;
+
+  /* bogus BIOSes may not return an error number */  
+  /* 0 sectors means no disk */
+  if (!(regs.ecx & 0x3f))
+    /* XXX 0x60 is one of the unused error numbers */
+    return 0x60;
+
+  /* the number of heads is counted from zero */
+  *heads = ((regs.edx >> 8) & 0xff) + 1;
+  *cylinders = (((regs.ecx >> 8) & 0xff) | ((regs.ecx << 2) & 0x0300)) + 1;
+  *sectors = regs.ecx & 0x3f;
+  return 0;
+}
+
+static int
+grub_biosdisk_get_diskinfo_real (int drive, void *drp, grub_uint16_t ax)
+{
+  struct grub_bios_int_registers regs;
+
+  regs.eax = ax;
+
+  /* compute the address of drive parameters */
+  regs.esi = ((grub_addr_t) drp) & 0xf;
+  regs.ds = ((grub_addr_t) drp) >> 4;
+  regs.edx = drive & 0xff;
+
+  regs.flags = GRUB_CPU_INT_FLAGS_DEFAULT;
+  grub_bios_interrupt (0x13, &regs);
+
+  /* Check if unsuccessful. Ignore return value if carry isn't set to 
+     workaround some buggy BIOSes. */
+  if ((regs.flags & GRUB_CPU_INT_FLAGS_CARRY) && ((regs.eax & 0xff00) != 0))
+    return (regs.eax & 0xff00) >> 8;
+
+  return 0;
+}
+
+/*
+ *   Return the cdrom information of DRIVE in CDRP. If an error occurs,
+ *   then return non-zero, otherwise zero.
+ */
+static int
+grub_biosdisk_get_cdinfo_int13_extensions (int drive, void *cdrp)
+{
+  return grub_biosdisk_get_diskinfo_real (drive, cdrp, 0x4b01);
+}
+
+/*
+ *   Return the geometry of DRIVE in a drive parameters, DRP. If an error
+ *   occurs, then return non-zero, otherwise zero.
+ */
+static int
+grub_biosdisk_get_diskinfo_int13_extensions (int drive, void *drp)
+{
+  return grub_biosdisk_get_diskinfo_real (drive, drp, 0x4800);
+}
+
+static int
+grub_biosdisk_get_drive (const char *name)
+{
+  unsigned long drive;
+
+  if (name[0] == 'c' && name[1] == 'd' && name[2] == 0 && cd_drive)
+    return cd_drive;
+
+  if ((name[0] != 'f' && name[0] != 'h') || name[1] != 'd')
+    goto fail;
+
+  drive = grub_strtoul (name + 2, 0, 10);
+  if (grub_errno != GRUB_ERR_NONE)
+    goto fail;
+
+  if (name[0] == 'h')
+    drive += 0x80;
+
+  return (int) drive ;
+
+ fail:
+  grub_error (GRUB_ERR_UNKNOWN_DEVICE, "not a biosdisk");
+  return -1;
+}
+
+static int
+grub_biosdisk_call_hook (grub_disk_dev_iterate_hook_t hook, void *hook_data,
+                        int drive)
+{
+  char name[10];
+
+  if (cd_drive && drive == cd_drive)
+    return hook ("cd", hook_data);
+
+  grub_snprintf (name, sizeof (name),
+                (drive & 0x80) ? "hd%d" : "fd%d", drive & (~0x80));
+  return hook (name, hook_data);
+}
+
+static int
+grub_biosdisk_iterate (grub_disk_dev_iterate_hook_t hook, void *hook_data,
+                      grub_disk_pull_t pull)
+{
+  int num_floppies;
+  int drive;
+
+  /* For hard disks, attempt to read the MBR.  */
+  switch (pull)
+    {
+    case GRUB_DISK_PULL_NONE:
+      for (drive = 0x80; drive < 0x90; drive++)
+       {
+         if (grub_biosdisk_rw_standard (0x02, drive, 0, 0, 1, 1,
+                                        GRUB_MEMORY_MACHINE_SCRATCH_SEG) != 0)
+           {
+             grub_dprintf ("disk", "Read error when probing drive 0x%2x\n", drive);
+             break;
+           }
+
+         if (grub_biosdisk_call_hook (hook, hook_data, drive))
+           return 1;
+       }
+      return 0;
+
+    case GRUB_DISK_PULL_REMOVABLE:
+      if (cd_drive)
+       {
+         if (grub_biosdisk_call_hook (hook, hook_data, cd_drive))
+           return 1;
+       }
+
+      /* For floppy disks, we can get the number safely.  */
+      num_floppies = grub_biosdisk_get_num_floppies ();
+      for (drive = 0; drive < num_floppies; drive++)
+       if (grub_biosdisk_call_hook (hook, hook_data, drive))
+         return 1;
+      return 0;
+    default:
+      return 0;
+    }
+  return 0;
+}
+
+#pragma pack(1)
+typedef struct ventoy_part_table
+{
+    grub_uint8_t  Active; // 0x00  0x80
+
+    grub_uint8_t  StartHead;
+    grub_uint16_t StartSector : 6;
+    grub_uint16_t StartCylinder : 10;
+
+    grub_uint8_t  FsFlag;
+
+    grub_uint8_t  EndHead;
+    grub_uint16_t EndSector : 6;
+    grub_uint16_t EndCylinder : 10;
+
+    grub_uint32_t StartSectorId;
+    grub_uint32_t SectorCount;
+}ventoy_part_table;
+
+typedef struct ventoy_mbr_head
+{
+    grub_uint8_t BootCode[446];
+    ventoy_part_table PartTbl[4];
+    grub_uint8_t Byte55;
+    grub_uint8_t ByteAA;
+}ventoy_mbr_head;
+#pragma pack()
+
+static grub_err_t
+grub_biosdisk_rw (int cmd, grub_disk_t disk,
+                 grub_disk_addr_t sector, grub_size_t size,
+                 unsigned segment);
+
+static int ventoy_is_mbr_match(ventoy_mbr_head *head)
+{
+    grub_uint32_t PartStartSector;
+    
+    if (head->Byte55 != 0x55 || head->ByteAA != 0xAA) {
+        return 0;
+    }
+    
+    if (head->PartTbl[2].SectorCount > 0 || head->PartTbl[3].SectorCount > 0) {
+        return 0;
+    }
+
+    if (head->PartTbl[0].FsFlag != 0x07 || head->PartTbl[0].StartSectorId != 2048) {
+        return 0;
+    }
+
+    if (head->PartTbl[1].Active != 0x80 || head->PartTbl[1].FsFlag != 0xEF) {
+        return 0;
+    }
+
+    PartStartSector = head->PartTbl[0].StartSectorId + head->PartTbl[0].SectorCount;
+
+    if (head->PartTbl[1].StartSectorId != PartStartSector || head->PartTbl[1].SectorCount != 65536) {
+        return 0;
+    }
+
+    return 1;
+}
+
+static grub_err_t
+grub_biosdisk_open (const char *name, grub_disk_t disk)
+{
+  grub_uint64_t total_sectors = 0;
+  int drive;
+  struct grub_biosdisk_data *data;
+
+  drive = grub_biosdisk_get_drive (name);
+  if (drive < 0)
+    return grub_errno;
+
+  disk->id = drive;
+
+  data = (struct grub_biosdisk_data *) grub_zalloc (sizeof (*data));
+  if (! data)
+    return grub_errno;
+
+  data->drive = drive;
+
+  if ((cd_drive) && (drive == cd_drive))
+    {
+      data->flags = GRUB_BIOSDISK_FLAG_LBA | GRUB_BIOSDISK_FLAG_CDROM;
+      data->sectors = 8;
+      disk->log_sector_size = 11;
+      /* TODO: get the correct size.  */
+      total_sectors = GRUB_DISK_SIZE_UNKNOWN;
+    }
+  else
+    {
+      /* HDD */
+      int version;
+
+      disk->log_sector_size = 9;
+
+      version = grub_biosdisk_check_int13_extensions (drive);
+      if (version)
+       {
+         struct grub_biosdisk_drp *drp
+           = (struct grub_biosdisk_drp *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
+
+         /* Clear out the DRP.  */
+         grub_memset (drp, 0, sizeof (*drp));
+         drp->size = sizeof (*drp);
+         if (! grub_biosdisk_get_diskinfo_int13_extensions (drive, drp))
+           {
+             data->flags = GRUB_BIOSDISK_FLAG_LBA;
+
+             if (drp->total_sectors)
+               total_sectors = drp->total_sectors;
+             else
+                /* Some buggy BIOSes doesn't return the total sectors
+                   correctly but returns zero. So if it is zero, compute
+                   it by C/H/S returned by the LBA BIOS call.  */
+                total_sectors = ((grub_uint64_t) drp->cylinders)
+                 * drp->heads * drp->sectors;
+             if (drp->bytes_per_sector
+                 && !(drp->bytes_per_sector & (drp->bytes_per_sector - 1))
+                 && drp->bytes_per_sector >= 512
+                 && drp->bytes_per_sector <= 16384)
+               {
+                 for (disk->log_sector_size = 0;
+                      (1 << disk->log_sector_size) < drp->bytes_per_sector;
+                      disk->log_sector_size++);
+               }
+           }
+       }
+    }
+
+  if (! (data->flags & GRUB_BIOSDISK_FLAG_CDROM))
+    {
+      if (grub_biosdisk_get_diskinfo_standard (drive,
+                                              &data->cylinders,
+                                              &data->heads,
+                                              &data->sectors) != 0)
+        {
+         if (total_sectors && (data->flags & GRUB_BIOSDISK_FLAG_LBA))
+           {
+             data->sectors = 63;
+             data->heads = 255;
+             data->cylinders
+               = grub_divmod64 (total_sectors
+                                + data->heads * data->sectors - 1,
+                                data->heads * data->sectors, 0);
+           }
+         else
+           {
+             grub_free (data);
+             return grub_error (GRUB_ERR_BAD_DEVICE, "%s cannot get C/H/S values", disk->name);
+           }
+        }
+
+      if (data->sectors == 0)
+       data->sectors = 63;
+      if (data->heads == 0)
+       data->heads = 255;
+
+      if (! total_sectors)
+        total_sectors = ((grub_uint64_t) data->cylinders)
+         * data->heads * data->sectors;
+    }
+
+  disk->total_sectors = total_sectors;
+  /* Limit the max to 0x7f because of Phoenix EDD.  */
+  disk->max_agglomerate = 0x7f >> GRUB_DISK_CACHE_BITS;
+  COMPILE_TIME_ASSERT ((0x7f >> GRUB_DISK_CACHE_BITS
+                       << (GRUB_DISK_SECTOR_BITS + GRUB_DISK_CACHE_BITS))
+                      + sizeof (struct grub_biosdisk_dap)
+                      < GRUB_MEMORY_MACHINE_SCRATCH_SIZE);
+
+  disk->data = data;
+
+  //fixup some buggy bios
+  if (total_sectors > (16434495 - 2097152) && total_sectors < (16434495 + 2097152) && 
+      (data->flags & GRUB_BIOSDISK_FLAG_LBA) > 0 && (data->flags & GRUB_BIOSDISK_FLAG_CDROM) == 0) {
+    if (grub_biosdisk_rw(0, disk, 0, 1, GRUB_MEMORY_MACHINE_SCRATCH_SEG) == 0) {
+        ventoy_mbr_head *mbr = (ventoy_mbr_head *)GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
+        if (ventoy_is_mbr_match(mbr)) {
+            total_sectors = mbr->PartTbl[1].StartSectorId + mbr->PartTbl[1].SectorCount + 1;
+            if (disk->total_sectors < total_sectors) {
+                disk->total_sectors = total_sectors;
+            }
+        }
+    }
+  }
+
+  return GRUB_ERR_NONE;
+}
+
+static void
+grub_biosdisk_close (grub_disk_t disk)
+{
+  grub_free (disk->data);
+}
+
+/* For readability.  */
+#define GRUB_BIOSDISK_READ     0
+#define GRUB_BIOSDISK_WRITE    1
+
+#define GRUB_BIOSDISK_CDROM_RETRY_COUNT 3
+
+static grub_err_t
+grub_biosdisk_rw (int cmd, grub_disk_t disk,
+                 grub_disk_addr_t sector, grub_size_t size,
+                 unsigned segment)
+{
+  struct grub_biosdisk_data *data = disk->data;
+
+  /* VirtualBox fails with sectors above 2T on CDs.
+     Since even BD-ROMS are never that big anyway, return error.  */
+  if ((data->flags & GRUB_BIOSDISK_FLAG_CDROM)
+      && (sector >> 32))
+    return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                      N_("attempt to read or write outside of disk `%s'"),
+                      disk->name);
+
+  if (data->flags & GRUB_BIOSDISK_FLAG_LBA)
+    {
+      struct grub_biosdisk_dap *dap;
+
+      dap = (struct grub_biosdisk_dap *) (GRUB_MEMORY_MACHINE_SCRATCH_ADDR
+                                         + (data->sectors
+                                            << disk->log_sector_size));
+      dap->length = sizeof (*dap);
+      dap->reserved = 0;
+      dap->blocks = size;
+      dap->buffer = segment << 16;     /* The format SEGMENT:ADDRESS.  */
+      dap->block = sector;
+
+      if (data->flags & GRUB_BIOSDISK_FLAG_CDROM)
+        {
+         int i;
+
+         if (cmd)
+           return grub_error (GRUB_ERR_WRITE_ERROR, N_("cannot write to CD-ROM"));
+
+         for (i = 0; i < GRUB_BIOSDISK_CDROM_RETRY_COUNT; i++)
+            if (! grub_biosdisk_rw_int13_extensions (0x42, data->drive, dap))
+             break;
+
+         if (i == GRUB_BIOSDISK_CDROM_RETRY_COUNT)
+           return grub_error (GRUB_ERR_READ_ERROR, N_("failure reading sector 0x%llx "
+                                                      "from `%s'"),
+                              (unsigned long long) sector,
+                              disk->name);
+       }
+      else
+        if (grub_biosdisk_rw_int13_extensions (cmd + 0x42, data->drive, dap))
+         {
+           /* Fall back to the CHS mode.  */
+           data->flags &= ~GRUB_BIOSDISK_FLAG_LBA;
+           disk->total_sectors = data->cylinders * data->heads * data->sectors;
+           return grub_biosdisk_rw (cmd, disk, sector, size, segment);
+         }
+    }
+  else
+    {
+      unsigned coff, hoff, soff;
+      unsigned head;
+
+      /* It is impossible to reach over 8064 MiB (a bit less than LBA24) with
+        the traditional CHS access.  */
+      if (sector >
+         1024 /* cylinders */ *
+         256 /* heads */ *
+         63 /* spt */)
+       return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                          N_("attempt to read or write outside of disk `%s'"),
+                          disk->name);
+
+      soff = ((grub_uint32_t) sector) % data->sectors + 1;
+      head = ((grub_uint32_t) sector) / data->sectors;
+      hoff = head % data->heads;
+      coff = head / data->heads;
+
+      if (coff >= data->cylinders)
+       return grub_error (GRUB_ERR_OUT_OF_RANGE,
+                          N_("attempt to read or write outside of disk `%s'"),
+                          disk->name);
+
+      if (grub_biosdisk_rw_standard (cmd + 0x02, data->drive,
+                                    coff, hoff, soff, size, segment))
+       {
+         switch (cmd)
+           {
+           case GRUB_BIOSDISK_READ:
+             return grub_error (GRUB_ERR_READ_ERROR, N_("failure reading sector 0x%llx "
+                                                        "from `%s'"),
+                                (unsigned long long) sector,
+                                disk->name);
+           case GRUB_BIOSDISK_WRITE:
+             return grub_error (GRUB_ERR_WRITE_ERROR, N_("failure writing sector 0x%llx "
+                                                         "to `%s'"),
+                                (unsigned long long) sector,
+                                disk->name);
+           }
+       }
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+/* Return the number of sectors which can be read safely at a time.  */
+static grub_size_t
+get_safe_sectors (grub_disk_t disk, grub_disk_addr_t sector)
+{
+  grub_size_t size;
+  grub_uint64_t offset;
+  struct grub_biosdisk_data *data = disk->data;
+  grub_uint32_t sectors = data->sectors;
+
+  /* OFFSET = SECTOR % SECTORS */
+  grub_divmod64 (sector, sectors, &offset);
+
+  size = sectors - offset;
+
+  return size;
+}
+
+static grub_err_t
+grub_biosdisk_read (grub_disk_t disk, grub_disk_addr_t sector,
+                   grub_size_t size, char *buf)
+{
+  while (size)
+    {
+      grub_size_t len;
+
+      len = get_safe_sectors (disk, sector);
+
+      if (len > size)
+       len = size;
+
+      if (grub_biosdisk_rw (GRUB_BIOSDISK_READ, disk, sector, len,
+                           GRUB_MEMORY_MACHINE_SCRATCH_SEG))
+       return grub_errno;
+
+      grub_memcpy (buf, (void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR,
+                  len << disk->log_sector_size);
+
+      buf += len << disk->log_sector_size;
+      sector += len;
+      size -= len;
+    }
+
+  return grub_errno;
+}
+
+static grub_err_t
+grub_biosdisk_write (grub_disk_t disk, grub_disk_addr_t sector,
+                    grub_size_t size, const char *buf)
+{
+  struct grub_biosdisk_data *data = disk->data;
+
+  if (data->flags & GRUB_BIOSDISK_FLAG_CDROM)
+    return grub_error (GRUB_ERR_IO, N_("cannot write to CD-ROM"));
+
+  while (size)
+    {
+      grub_size_t len;
+
+      len = get_safe_sectors (disk, sector);
+      if (len > size)
+       len = size;
+
+      grub_memcpy ((void *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR, buf,
+                  len << disk->log_sector_size);
+
+      if (grub_biosdisk_rw (GRUB_BIOSDISK_WRITE, disk, sector, len,
+                           GRUB_MEMORY_MACHINE_SCRATCH_SEG))
+       return grub_errno;
+
+      buf += len << disk->log_sector_size;
+      sector += len;
+      size -= len;
+    }
+
+  return grub_errno;
+}
+
+static struct grub_disk_dev grub_biosdisk_dev =
+  {
+    .name = "biosdisk",
+    .id = GRUB_DISK_DEVICE_BIOSDISK_ID,
+    .disk_iterate = grub_biosdisk_iterate,
+    .disk_open = grub_biosdisk_open,
+    .disk_close = grub_biosdisk_close,
+    .disk_read = grub_biosdisk_read,
+    .disk_write = grub_biosdisk_write,
+    .next = 0
+  };
+
+static void
+grub_disk_biosdisk_fini (void)
+{
+  grub_disk_dev_unregister (&grub_biosdisk_dev);
+}
+
+GRUB_MOD_INIT(biosdisk)
+{
+  struct grub_biosdisk_cdrp *cdrp
+    = (struct grub_biosdisk_cdrp *) GRUB_MEMORY_MACHINE_SCRATCH_ADDR;
+  grub_uint8_t boot_drive;
+
+  if (grub_disk_firmware_is_tainted)
+    {
+      grub_puts_ (N_("Native disk drivers are in use. "
+                    "Refusing to use firmware disk interface."));
+      return;
+    }
+  grub_disk_firmware_fini = grub_disk_biosdisk_fini;
+
+  grub_memset (cdrp, 0, sizeof (*cdrp));
+  cdrp->size = sizeof (*cdrp);
+  cdrp->media_type = 0xFF;
+  boot_drive = (grub_boot_device >> 24);
+  if ((! grub_biosdisk_get_cdinfo_int13_extensions (boot_drive, cdrp))
+      && ((cdrp->media_type & GRUB_BIOSDISK_CDTYPE_MASK)
+         == GRUB_BIOSDISK_CDTYPE_NO_EMUL))
+    cd_drive = cdrp->drive_no;
+  /* Since diskboot.S rejects devices over 0x90 it must be a CD booted with
+     cdboot.S
+   */
+  if (boot_drive >= 0x90)
+    cd_drive = boot_drive;
+
+  grub_disk_dev_register (&grub_biosdisk_dev);
+}
+
+GRUB_MOD_FINI(biosdisk)
+{
+  grub_disk_biosdisk_fini ();
+}
diff --git a/GRUB2/grub-2.04/grub-core/fs/fat.c b/GRUB2/grub-2.04/grub-core/fs/fat.c
new file mode 100644 (file)
index 0000000..2b26a8c
--- /dev/null
@@ -0,0 +1,1448 @@
+/* fat.c - FAT filesystem */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2000,2001,2002,2003,2004,2005,2007,2008,2009  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/fs.h>
+#include <grub/disk.h>
+#include <grub/file.h>
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/charset.h>
+#ifndef MODE_EXFAT
+#include <grub/fat.h>
+#else
+#include <grub/exfat.h>
+#endif
+#include <grub/fshelp.h>
+#include <grub/i18n.h>
+#include <grub/time.h>
+#include <grub/ventoy.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+enum
+  {
+    GRUB_FAT_ATTR_READ_ONLY = 0x01,
+    GRUB_FAT_ATTR_HIDDEN = 0x02,
+    GRUB_FAT_ATTR_SYSTEM = 0x04,
+#ifndef MODE_EXFAT
+    GRUB_FAT_ATTR_VOLUME_ID = 0x08,
+#endif
+    GRUB_FAT_ATTR_DIRECTORY = 0x10,
+    GRUB_FAT_ATTR_ARCHIVE = 0x20,
+
+#ifndef MODE_EXFAT
+    GRUB_FAT_ATTR_LONG_NAME = (GRUB_FAT_ATTR_READ_ONLY
+                              | GRUB_FAT_ATTR_HIDDEN
+                              | GRUB_FAT_ATTR_SYSTEM
+                              | GRUB_FAT_ATTR_VOLUME_ID),
+#endif
+    GRUB_FAT_ATTR_VALID = (GRUB_FAT_ATTR_READ_ONLY
+                          | GRUB_FAT_ATTR_HIDDEN
+                          | GRUB_FAT_ATTR_SYSTEM
+                          | GRUB_FAT_ATTR_DIRECTORY
+                          | GRUB_FAT_ATTR_ARCHIVE
+#ifndef MODE_EXFAT
+                          | GRUB_FAT_ATTR_VOLUME_ID
+#endif
+                          )
+  };
+
+#ifdef MODE_EXFAT
+typedef struct grub_exfat_bpb grub_current_fat_bpb_t;
+#else
+typedef struct grub_fat_bpb grub_current_fat_bpb_t;
+#endif
+
+#ifdef MODE_EXFAT
+enum
+  {
+    FLAG_CONTIGUOUS = 2
+  };
+struct grub_fat_dir_entry
+{
+  grub_uint8_t entry_type;
+  union
+  {
+    grub_uint8_t placeholder[31];
+    struct {
+      grub_uint8_t secondary_count;
+      grub_uint16_t checksum;
+      grub_uint16_t attr;
+      grub_uint16_t reserved1;
+      grub_uint32_t c_time;
+      grub_uint32_t m_time;
+      grub_uint32_t a_time;
+      grub_uint8_t c_time_tenth;
+      grub_uint8_t m_time_tenth;
+      grub_uint8_t a_time_tenth;
+      grub_uint8_t reserved2[9];
+    }  GRUB_PACKED file;
+    struct {
+      grub_uint8_t flags;
+      grub_uint8_t reserved1;
+      grub_uint8_t name_length;
+      grub_uint16_t name_hash;
+      grub_uint16_t reserved2;
+      grub_uint64_t valid_size;
+      grub_uint32_t reserved3;
+      grub_uint32_t first_cluster;
+      grub_uint64_t file_size;
+    }   GRUB_PACKED stream_extension;
+    struct {
+      grub_uint8_t flags;
+      grub_uint16_t str[15];
+    }  GRUB_PACKED  file_name;
+    struct {
+      grub_uint8_t character_count;
+      grub_uint16_t str[15];
+    }  GRUB_PACKED  volume_label;
+  }  GRUB_PACKED type_specific;
+} GRUB_PACKED;
+
+struct grub_fat_dir_node
+{
+  grub_uint32_t attr;
+  grub_uint32_t first_cluster;
+  grub_uint64_t file_size;
+  grub_uint64_t valid_size;
+  int have_stream;
+  int is_contiguous;
+};
+
+typedef struct grub_fat_dir_node grub_fat_dir_node_t;
+
+#else
+struct grub_fat_dir_entry
+{
+  grub_uint8_t name[11];
+  grub_uint8_t attr;
+  grub_uint8_t nt_reserved;
+  grub_uint8_t c_time_tenth;
+  grub_uint16_t c_time;
+  grub_uint16_t c_date;
+  grub_uint16_t a_date;
+  grub_uint16_t first_cluster_high;
+  grub_uint16_t w_time;
+  grub_uint16_t w_date;
+  grub_uint16_t first_cluster_low;
+  grub_uint32_t file_size;
+} GRUB_PACKED;
+
+struct grub_fat_long_name_entry
+{
+  grub_uint8_t id;
+  grub_uint16_t name1[5];
+  grub_uint8_t attr;
+  grub_uint8_t reserved;
+  grub_uint8_t checksum;
+  grub_uint16_t name2[6];
+  grub_uint16_t first_cluster;
+  grub_uint16_t name3[2];
+} GRUB_PACKED;
+
+typedef struct grub_fat_dir_entry grub_fat_dir_node_t;
+
+#endif
+
+struct grub_fat_data
+{
+  int logical_sector_bits;
+  grub_uint32_t num_sectors;
+
+  grub_uint32_t fat_sector;
+  grub_uint32_t sectors_per_fat;
+  int fat_size;
+
+  grub_uint32_t root_cluster;
+#ifndef MODE_EXFAT
+  grub_uint32_t root_sector;
+  grub_uint32_t num_root_sectors;
+#endif
+
+  int cluster_bits;
+  grub_uint32_t cluster_eof_mark;
+  grub_uint32_t cluster_sector;
+  grub_uint32_t num_clusters;
+
+  grub_uint32_t uuid;
+};
+
+struct grub_fshelp_node {
+  grub_disk_t disk;
+  struct grub_fat_data *data;
+
+  grub_uint8_t attr;
+#ifndef MODE_EXFAT
+  grub_uint32_t file_size;
+#else
+  grub_uint64_t file_size;
+#endif
+  grub_uint32_t file_cluster;
+  grub_uint32_t cur_cluster_num;
+  grub_uint32_t cur_cluster;
+
+#ifdef MODE_EXFAT
+  int is_contiguous;
+#endif
+};
+
+static grub_dl_t my_mod;
+
+#ifndef MODE_EXFAT
+static int
+fat_log2 (unsigned x)
+{
+  int i;
+
+  if (x == 0)
+    return -1;
+
+  for (i = 0; (x & 1) == 0; i++)
+    x >>= 1;
+
+  if (x != 1)
+    return -1;
+
+  return i;
+}
+#endif
+
+static struct grub_fat_data *
+grub_fat_mount (grub_disk_t disk)
+{
+  grub_current_fat_bpb_t bpb;
+  struct grub_fat_data *data = 0;
+  grub_uint32_t first_fat, magic;
+
+  if (! disk)
+    goto fail;
+
+  data = (struct grub_fat_data *) grub_malloc (sizeof (*data));
+  if (! data)
+    goto fail;
+
+  /* Read the BPB.  */
+  if (grub_disk_read (disk, 0, 0, sizeof (bpb), &bpb))
+    goto fail;
+
+#ifdef MODE_EXFAT
+  if (grub_memcmp ((const char *) bpb.oem_name, "EXFAT   ",
+                  sizeof (bpb.oem_name)) != 0)
+    goto fail;    
+#endif
+
+  /* Get the sizes of logical sectors and clusters.  */
+#ifdef MODE_EXFAT
+  data->logical_sector_bits = bpb.bytes_per_sector_shift;
+#else
+  data->logical_sector_bits =
+    fat_log2 (grub_le_to_cpu16 (bpb.bytes_per_sector));
+#endif
+  if (data->logical_sector_bits < GRUB_DISK_SECTOR_BITS
+      || data->logical_sector_bits >= 16)
+    goto fail;
+  data->logical_sector_bits -= GRUB_DISK_SECTOR_BITS;
+
+#ifdef MODE_EXFAT
+  data->cluster_bits = bpb.sectors_per_cluster_shift;
+#else
+  data->cluster_bits = fat_log2 (bpb.sectors_per_cluster);
+#endif
+  if (data->cluster_bits < 0 || data->cluster_bits > 25)
+    goto fail;
+  data->cluster_bits += data->logical_sector_bits;
+
+  /* Get information about FATs.  */
+#ifdef MODE_EXFAT
+  data->fat_sector = (grub_le_to_cpu32 (bpb.num_reserved_sectors)
+                     << data->logical_sector_bits);
+#else
+  data->fat_sector = (grub_le_to_cpu16 (bpb.num_reserved_sectors)
+                     << data->logical_sector_bits);
+#endif
+  if (data->fat_sector == 0)
+    goto fail;
+
+#ifdef MODE_EXFAT
+  data->sectors_per_fat = (grub_le_to_cpu32 (bpb.sectors_per_fat)
+                          << data->logical_sector_bits);
+#else
+  data->sectors_per_fat = ((bpb.sectors_per_fat_16
+                           ? grub_le_to_cpu16 (bpb.sectors_per_fat_16)
+                           : grub_le_to_cpu32 (bpb.version_specific.fat32.sectors_per_fat_32))
+                          << data->logical_sector_bits);
+#endif
+  if (data->sectors_per_fat == 0)
+    goto fail;
+
+  /* Get the number of sectors in this volume.  */
+#ifdef MODE_EXFAT
+  data->num_sectors = ((grub_le_to_cpu64 (bpb.num_total_sectors))
+                      << data->logical_sector_bits);
+#else
+  data->num_sectors = ((bpb.num_total_sectors_16
+                       ? grub_le_to_cpu16 (bpb.num_total_sectors_16)
+                       : grub_le_to_cpu32 (bpb.num_total_sectors_32))
+                      << data->logical_sector_bits);
+#endif
+  if (data->num_sectors == 0)
+    goto fail;
+
+  /* Get information about the root directory.  */
+  if (bpb.num_fats == 0)
+    goto fail;
+
+#ifndef MODE_EXFAT
+  data->root_sector = data->fat_sector + bpb.num_fats * data->sectors_per_fat;
+  data->num_root_sectors
+    = ((((grub_uint32_t) grub_le_to_cpu16 (bpb.num_root_entries)
+        * sizeof (struct grub_fat_dir_entry)
+        + grub_le_to_cpu16 (bpb.bytes_per_sector) - 1)
+       >> (data->logical_sector_bits + GRUB_DISK_SECTOR_BITS))
+       << (data->logical_sector_bits));
+#endif
+
+#ifdef MODE_EXFAT
+  data->cluster_sector = (grub_le_to_cpu32 (bpb.cluster_offset) 
+                         << data->logical_sector_bits);
+  data->num_clusters = (grub_le_to_cpu32 (bpb.cluster_count)
+                         << data->logical_sector_bits);
+#else
+  data->cluster_sector = data->root_sector + data->num_root_sectors;
+  data->num_clusters = (((data->num_sectors - data->cluster_sector)
+                        >> data->cluster_bits)
+                       + 2);
+#endif
+
+  if (data->num_clusters <= 2)
+    goto fail;
+
+#ifdef MODE_EXFAT
+  {
+    /* exFAT.  */
+    data->root_cluster = grub_le_to_cpu32 (bpb.root_cluster);
+    data->fat_size = 32;
+    data->cluster_eof_mark = 0xffffffff;
+
+    if ((bpb.volume_flags & grub_cpu_to_le16_compile_time (0x1))
+       && bpb.num_fats > 1)
+      data->fat_sector += data->sectors_per_fat;
+  }
+#else
+  if (! bpb.sectors_per_fat_16)
+    {
+      /* FAT32.  */
+      grub_uint16_t flags = grub_le_to_cpu16 (bpb.version_specific.fat32.extended_flags);
+
+      data->root_cluster = grub_le_to_cpu32 (bpb.version_specific.fat32.root_cluster);
+      data->fat_size = 32;
+      data->cluster_eof_mark = 0x0ffffff8;
+
+      if (flags & 0x80)
+       {
+         /* Get an active FAT.  */
+         unsigned active_fat = flags & 0xf;
+
+         if (active_fat > bpb.num_fats)
+           goto fail;
+
+         data->fat_sector += active_fat * data->sectors_per_fat;
+       }
+
+      if (bpb.num_root_entries != 0 || bpb.version_specific.fat32.fs_version != 0)
+       goto fail;
+    }
+  else
+    {
+      /* FAT12 or FAT16.  */
+      data->root_cluster = ~0U;
+
+      if (data->num_clusters <= 4085 + 2)
+       {
+         /* FAT12.  */
+         data->fat_size = 12;
+         data->cluster_eof_mark = 0x0ff8;
+       }
+      else
+       {
+         /* FAT16.  */
+         data->fat_size = 16;
+         data->cluster_eof_mark = 0xfff8;
+       }
+    }
+#endif
+
+  /* More sanity checks.  */
+  if (data->num_sectors <= data->fat_sector)
+    goto fail;
+
+  if (grub_disk_read (disk,
+                     data->fat_sector,
+                     0,
+                     sizeof (first_fat),
+                     &first_fat))
+    goto fail;
+
+  first_fat = grub_le_to_cpu32 (first_fat);
+
+  if (data->fat_size == 32)
+    {
+      first_fat &= 0x0fffffff;
+      magic = 0x0fffff00;
+    }
+  else if (data->fat_size == 16)
+    {
+      first_fat &= 0x0000ffff;
+      magic = 0xff00;
+    }
+  else
+    {
+      first_fat &= 0x00000fff;
+      magic = 0x0f00;
+    }
+
+  /* Serial number.  */
+#ifdef MODE_EXFAT
+    data->uuid = grub_le_to_cpu32 (bpb.num_serial);
+#else
+  if (bpb.sectors_per_fat_16)
+    data->uuid = grub_le_to_cpu32 (bpb.version_specific.fat12_or_fat16.num_serial);
+  else
+    data->uuid = grub_le_to_cpu32 (bpb.version_specific.fat32.num_serial);
+#endif
+
+#ifndef MODE_EXFAT
+  /* Ignore the 3rd bit, because some BIOSes assigns 0xF0 to the media
+     descriptor, even if it is a so-called superfloppy (e.g. an USB key).
+     The check may be too strict for this kind of stupid BIOSes, as
+     they overwrite the media descriptor.  */
+  if ((first_fat | 0x8) != (magic | bpb.media | 0x8))
+    goto fail;
+#else
+  (void) magic;
+#endif
+
+  return data;
+
+ fail:
+
+  grub_free (data);
+  grub_error (GRUB_ERR_BAD_FS, "not a FAT filesystem");
+  return 0;
+}
+
+static grub_ssize_t
+grub_fat_read_data (grub_disk_t disk, grub_fshelp_node_t node,
+                   grub_disk_read_hook_t read_hook, void *read_hook_data,
+                   grub_off_t offset, grub_size_t len, char *buf)
+{
+  grub_size_t size;
+  grub_uint32_t logical_cluster;
+  unsigned logical_cluster_bits;
+  grub_ssize_t ret = 0;
+  unsigned long sector;
+
+#ifndef MODE_EXFAT
+  /* This is a special case. FAT12 and FAT16 doesn't have the root directory
+     in clusters.  */
+  if (node->file_cluster == ~0U)
+    {
+      size = (node->data->num_root_sectors << GRUB_DISK_SECTOR_BITS) - offset;
+      if (size > len)
+       size = len;
+
+      if (grub_disk_read (disk, node->data->root_sector, offset, size, buf))
+       return -1;
+
+      return size;
+    }
+#endif
+
+#ifdef MODE_EXFAT
+  if (node->is_contiguous)
+    {
+      /* Read the data here.  */
+      sector = (node->data->cluster_sector
+               + ((node->file_cluster - 2)
+                  << node->data->cluster_bits));
+
+      disk->read_hook = read_hook;
+      disk->read_hook_data = read_hook_data;
+      grub_disk_read (disk, sector + (offset >> GRUB_DISK_SECTOR_BITS),
+                     offset & (GRUB_DISK_SECTOR_SIZE - 1), len, buf);
+      disk->read_hook = 0;
+      if (grub_errno)
+       return -1;
+
+      return len;
+    }
+#endif
+
+  /* Calculate the logical cluster number and offset.  */
+  logical_cluster_bits = (node->data->cluster_bits
+                         + GRUB_DISK_SECTOR_BITS);
+  logical_cluster = offset >> logical_cluster_bits;
+  offset &= (1ULL << logical_cluster_bits) - 1;
+
+  if (logical_cluster < node->cur_cluster_num)
+    {
+      node->cur_cluster_num = 0;
+      node->cur_cluster = node->file_cluster;
+    }
+
+  while (len)
+    {
+      while (logical_cluster > node->cur_cluster_num)
+       {
+         /* Find next cluster.  */
+         grub_uint32_t next_cluster;
+         grub_uint32_t fat_offset;
+
+         switch (node->data->fat_size)
+           {
+           case 32:
+             fat_offset = node->cur_cluster << 2;
+             break;
+           case 16:
+             fat_offset = node->cur_cluster << 1;
+             break;
+           default:
+             /* case 12: */
+             fat_offset = node->cur_cluster + (node->cur_cluster >> 1);
+             break;
+           }
+
+         /* Read the FAT.  */
+         if (grub_disk_read (disk, node->data->fat_sector, fat_offset,
+                             (node->data->fat_size + 7) >> 3,
+                             (char *) &next_cluster))
+           return -1;
+
+         next_cluster = grub_le_to_cpu32 (next_cluster);
+         switch (node->data->fat_size)
+           {
+           case 16:
+             next_cluster &= 0xFFFF;
+             break;
+           case 12:
+             if (node->cur_cluster & 1)
+               next_cluster >>= 4;
+
+             next_cluster &= 0x0FFF;
+             break;
+           }
+
+         grub_dprintf ("fat", "fat_size=%d, next_cluster=%u\n",
+                       node->data->fat_size, next_cluster);
+
+         /* Check the end.  */
+         if (next_cluster >= node->data->cluster_eof_mark)
+           return ret;
+
+         if (next_cluster < 2 || next_cluster >= node->data->num_clusters)
+           {
+             grub_error (GRUB_ERR_BAD_FS, "invalid cluster %u",
+                         next_cluster);
+             return -1;
+           }
+
+         node->cur_cluster = next_cluster;
+         node->cur_cluster_num++;
+       }
+
+      /* Read the data here.  */
+      sector = (node->data->cluster_sector
+               + ((node->cur_cluster - 2)
+                  << node->data->cluster_bits));
+      size = (1 << logical_cluster_bits) - offset;
+      if (size > len)
+       size = len;
+
+      disk->read_hook = read_hook;
+      disk->read_hook_data = read_hook_data;
+      grub_disk_read (disk, sector, offset, size, buf);
+      disk->read_hook = 0;
+      if (grub_errno)
+       return -1;
+
+      len -= size;
+      buf += size;
+      ret += size;
+      logical_cluster++;
+      offset = 0;
+    }
+
+  return ret;
+}
+
+struct grub_fat_iterate_context
+{
+#ifdef MODE_EXFAT
+  struct grub_fat_dir_node dir;
+#else
+  struct grub_fat_dir_entry dir;
+#endif
+  char *filename;
+  grub_uint16_t *unibuf;
+  grub_ssize_t offset;
+};
+
+static grub_err_t
+grub_fat_iterate_init (struct grub_fat_iterate_context *ctxt)
+{
+  ctxt->offset = -sizeof (struct grub_fat_dir_entry);
+
+#ifndef MODE_EXFAT
+  /* Allocate space enough to hold a long name.  */
+  ctxt->filename = grub_malloc (0x40 * 13 * GRUB_MAX_UTF8_PER_UTF16 + 1);
+  ctxt->unibuf = (grub_uint16_t *) grub_malloc (0x40 * 13 * 2);
+#else
+  ctxt->unibuf = grub_malloc (15 * 256 * 2);
+  ctxt->filename = grub_malloc (15 * 256 * GRUB_MAX_UTF8_PER_UTF16 + 1);
+#endif
+
+  if (! ctxt->filename || ! ctxt->unibuf)
+    {
+      grub_free (ctxt->filename);
+      grub_free (ctxt->unibuf);
+      return grub_errno;
+    }
+  return GRUB_ERR_NONE;
+}
+
+static void
+grub_fat_iterate_fini (struct grub_fat_iterate_context *ctxt)
+{
+  grub_free (ctxt->filename);
+  grub_free (ctxt->unibuf);
+}
+
+#ifdef MODE_EXFAT
+static grub_err_t
+grub_fat_iterate_dir_next (grub_fshelp_node_t node,
+                          struct grub_fat_iterate_context *ctxt)
+{
+  grub_memset (&ctxt->dir, 0, sizeof (ctxt->dir));
+  while (1)
+    {
+      struct grub_fat_dir_entry dir;
+
+      ctxt->offset += sizeof (dir);
+
+      if (grub_fat_read_data (node->disk, node, 0, 0, ctxt->offset, sizeof (dir),
+                             (char *) &dir)
+          != sizeof (dir))
+       break;
+
+      if (dir.entry_type == 0)
+       break;
+      if (!(dir.entry_type & 0x80))
+       continue;
+
+      if (dir.entry_type == 0x85)
+       {
+         unsigned i, nsec, slots = 0;
+
+         nsec = dir.type_specific.file.secondary_count;
+
+         ctxt->dir.attr = grub_cpu_to_le16 (dir.type_specific.file.attr);
+         ctxt->dir.have_stream = 0;
+         for (i = 0; i < nsec; i++)
+           {
+             struct grub_fat_dir_entry sec;
+             ctxt->offset += sizeof (sec);
+             if (grub_fat_read_data (node->disk, node, 0, 0,
+                                     ctxt->offset, sizeof (sec), (char *) &sec)
+                 != sizeof (sec))
+               break;
+             if (!(sec.entry_type & 0x80))
+               continue;
+             if (!(sec.entry_type & 0x40))
+               break;
+             switch (sec.entry_type)
+               {
+               case 0xc0:
+                 ctxt->dir.first_cluster = grub_cpu_to_le32 (sec.type_specific.stream_extension.first_cluster);
+                 ctxt->dir.valid_size
+                   = grub_cpu_to_le64 (sec.type_specific.stream_extension.valid_size);
+                 ctxt->dir.file_size
+                   = grub_cpu_to_le64 (sec.type_specific.stream_extension.file_size);
+                 ctxt->dir.have_stream = 1;
+                 ctxt->dir.is_contiguous = !!(sec.type_specific.stream_extension.flags
+                                              & grub_cpu_to_le16_compile_time (FLAG_CONTIGUOUS));
+                 break;
+               case 0xc1:
+                 {
+                   int j;
+                   for (j = 0; j < 15; j++)
+                     ctxt->unibuf[slots * 15 + j] 
+                       = grub_le_to_cpu16 (sec.type_specific.file_name.str[j]);
+                   slots++;
+                 }
+                 break;
+               default:
+                 grub_dprintf ("exfat", "unknown secondary type 0x%02x\n",
+                               sec.entry_type);
+               }
+           }
+
+         if (i != nsec)
+           {
+             ctxt->offset -= sizeof (dir);
+             continue;
+           }
+
+         *grub_utf16_to_utf8 ((grub_uint8_t *) ctxt->filename, ctxt->unibuf,
+                              slots * 15) = '\0';
+
+         return 0;
+       }
+      /* Allocation bitmap. */
+      if (dir.entry_type == 0x81)
+       continue;
+      /* Upcase table. */
+      if (dir.entry_type == 0x82)
+       continue;
+      /* Volume label. */
+      if (dir.entry_type == 0x83)
+       continue;
+      grub_dprintf ("exfat", "unknown primary type 0x%02x\n",
+                   dir.entry_type);
+    }
+  return grub_errno ? : GRUB_ERR_EOF;
+}
+
+#else
+
+static grub_err_t
+grub_fat_iterate_dir_next (grub_fshelp_node_t node,
+                          struct grub_fat_iterate_context *ctxt)
+{
+  char *filep = 0;
+  int checksum = -1;
+  int slot = -1, slots = -1;
+
+  while (1)
+    {
+      unsigned i;
+
+      /* Adjust the offset.  */
+      ctxt->offset += sizeof (ctxt->dir);
+
+      /* Read a directory entry.  */
+      if (grub_fat_read_data (node->disk, node, 0, 0,
+                             ctxt->offset, sizeof (ctxt->dir),
+                             (char *) &ctxt->dir)
+          != sizeof (ctxt->dir) || ctxt->dir.name[0] == 0)
+       break;
+
+      /* Handle long name entries.  */
+      if (ctxt->dir.attr == GRUB_FAT_ATTR_LONG_NAME)
+       {
+         struct grub_fat_long_name_entry *long_name
+           = (struct grub_fat_long_name_entry *) &ctxt->dir;
+         grub_uint8_t id = long_name->id;
+
+         if (id & 0x40)
+           {
+             id &= 0x3f;
+             slots = slot = id;
+             checksum = long_name->checksum;
+           }
+
+         if (id != slot || slot == 0 || checksum != long_name->checksum)
+           {
+             checksum = -1;
+             continue;
+           }
+
+         slot--;
+         grub_memcpy (ctxt->unibuf + slot * 13, long_name->name1, 5 * 2);
+         grub_memcpy (ctxt->unibuf + slot * 13 + 5, long_name->name2, 6 * 2);
+         grub_memcpy (ctxt->unibuf + slot * 13 + 11, long_name->name3, 2 * 2);
+         continue;
+       }
+
+      /* Check if this entry is valid.  */
+      if (ctxt->dir.name[0] == 0xe5 || (ctxt->dir.attr & ~GRUB_FAT_ATTR_VALID))
+       continue;
+
+      /* This is a workaround for Japanese.  */
+      if (ctxt->dir.name[0] == 0x05)
+       ctxt->dir.name[0] = 0xe5;
+
+      if (checksum != -1 && slot == 0)
+       {
+         grub_uint8_t sum;
+
+         for (sum = 0, i = 0; i < sizeof (ctxt->dir.name); i++)
+           sum = ((sum >> 1) | (sum << 7)) + ctxt->dir.name[i];
+
+         if (sum == checksum)
+           {
+             int u;
+
+             for (u = 0; u < slots * 13; u++)
+               ctxt->unibuf[u] = grub_le_to_cpu16 (ctxt->unibuf[u]);
+
+             *grub_utf16_to_utf8 ((grub_uint8_t *) ctxt->filename,
+                                  ctxt->unibuf,
+                                  slots * 13) = '\0';
+
+             return GRUB_ERR_NONE;
+           }
+
+         checksum = -1;
+       }
+
+      /* Convert the 8.3 file name.  */
+      filep = ctxt->filename;
+      if (ctxt->dir.attr & GRUB_FAT_ATTR_VOLUME_ID)
+       {
+         for (i = 0; i < sizeof (ctxt->dir.name) && ctxt->dir.name[i]; i++)
+           *filep++ = ctxt->dir.name[i];
+         while (i > 0 && ctxt->dir.name[i - 1] == ' ')
+           {
+             filep--;
+             i--;
+           }
+       }
+      else
+       {
+         for (i = 0; i < 8 && ctxt->dir.name[i]; i++)
+           *filep++ = grub_tolower (ctxt->dir.name[i]);
+         while (i > 0 && ctxt->dir.name[i - 1] == ' ')
+           {
+             filep--;
+             i--;
+           }
+
+         /* XXX should we check that dir position is 0 or 1? */
+         if (i > 2 || filep[0] != '.' || (i == 2 && filep[1] != '.'))
+           *filep++ = '.';
+
+         for (i = 8; i < 11 && ctxt->dir.name[i]; i++)
+           *filep++ = grub_tolower (ctxt->dir.name[i]);
+         while (i > 8 && ctxt->dir.name[i - 1] == ' ')
+           {
+             filep--;
+             i--;
+           }
+
+         if (i == 8)
+           filep--;
+       }
+      *filep = '\0';
+      return GRUB_ERR_NONE;
+    }
+
+  return grub_errno ? : GRUB_ERR_EOF;
+}
+
+#endif
+
+static grub_err_t lookup_file (grub_fshelp_node_t node,
+                              const char *name,
+                              grub_fshelp_node_t *foundnode,
+                              enum grub_fshelp_filetype *foundtype)
+{
+  grub_err_t err;
+  struct grub_fat_iterate_context ctxt;
+
+  err = grub_fat_iterate_init (&ctxt);
+  if (err)
+    return err;
+
+  while (!(err = grub_fat_iterate_dir_next (node, &ctxt)))
+    {
+
+#ifdef MODE_EXFAT
+      if (!ctxt.dir.have_stream)
+       continue;
+#else
+      if (ctxt.dir.attr & GRUB_FAT_ATTR_VOLUME_ID)
+       continue;
+#endif
+
+      if (grub_strcasecmp (name, ctxt.filename) == 0)
+       {
+         *foundnode = grub_malloc (sizeof (struct grub_fshelp_node));
+         if (!*foundnode)
+           return grub_errno;
+         (*foundnode)->attr = ctxt.dir.attr;
+#ifdef MODE_EXFAT
+         (*foundnode)->file_size = ctxt.dir.file_size;
+         (*foundnode)->file_cluster = ctxt.dir.first_cluster;
+         (*foundnode)->is_contiguous = ctxt.dir.is_contiguous;
+#else
+         (*foundnode)->file_size = grub_le_to_cpu32 (ctxt.dir.file_size);
+         (*foundnode)->file_cluster = ((grub_le_to_cpu16 (ctxt.dir.first_cluster_high) << 16)
+                               | grub_le_to_cpu16 (ctxt.dir.first_cluster_low));
+         /* If directory points to root, starting cluster is 0 */
+         if (!(*foundnode)->file_cluster)
+           (*foundnode)->file_cluster = node->data->root_cluster;
+#endif
+         (*foundnode)->cur_cluster_num = ~0U;
+         (*foundnode)->data = node->data;
+         (*foundnode)->disk = node->disk;
+
+         *foundtype = ((*foundnode)->attr & GRUB_FAT_ATTR_DIRECTORY) ? GRUB_FSHELP_DIR : GRUB_FSHELP_REG;
+
+         grub_fat_iterate_fini (&ctxt);
+         return GRUB_ERR_NONE;
+       }
+    }
+
+  grub_fat_iterate_fini (&ctxt);
+  if (err == GRUB_ERR_EOF)
+    err = 0;
+
+  return err;
+
+}
+
+static grub_err_t
+grub_fat_dir (grub_device_t device, const char *path, grub_fs_dir_hook_t hook,
+             void *hook_data)
+{
+  struct grub_fat_data *data = 0;
+  grub_disk_t disk = device->disk;
+  grub_fshelp_node_t found = NULL;
+  grub_err_t err;
+  struct grub_fat_iterate_context ctxt;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_fat_mount (disk);
+  if (! data)
+    goto fail;
+
+  struct grub_fshelp_node root = {
+    .data = data,
+    .disk = disk,
+    .attr = GRUB_FAT_ATTR_DIRECTORY,
+    .file_size = 0,
+    .file_cluster = data->root_cluster,
+    .cur_cluster_num = ~0U,
+    .cur_cluster = 0,
+#ifdef MODE_EXFAT
+    .is_contiguous = 0,
+#endif
+  };
+
+  err = grub_fshelp_find_file_lookup (path, &root, &found, lookup_file, NULL, GRUB_FSHELP_DIR);
+  if (err)
+    goto fail;
+
+  err = grub_fat_iterate_init (&ctxt);
+  if (err)
+    goto fail;
+
+  while (!(err = grub_fat_iterate_dir_next (found, &ctxt)))
+    {
+      struct grub_dirhook_info info;
+      grub_memset (&info, 0, sizeof (info));
+
+      info.dir = !! (ctxt.dir.attr & GRUB_FAT_ATTR_DIRECTORY);
+      info.case_insensitive = 1;
+#ifdef MODE_EXFAT
+      if (!ctxt.dir.have_stream)
+       continue;
+#else
+      if (ctxt.dir.attr & GRUB_FAT_ATTR_VOLUME_ID)
+       continue;
+#endif
+
+      if (hook (ctxt.filename, &info, hook_data))
+       break;
+    }
+  grub_fat_iterate_fini (&ctxt);
+  if (err == GRUB_ERR_EOF)
+    err = 0;
+
+ fail:
+  if (found != &root)
+    grub_free (found);
+
+  grub_free (data);
+
+  grub_dl_unref (my_mod);
+
+  return grub_errno;
+}
+
+static grub_err_t
+grub_fat_open (grub_file_t file, const char *name)
+{
+  struct grub_fat_data *data = 0;
+  grub_fshelp_node_t found = NULL;
+  grub_err_t err;
+  grub_disk_t disk = file->device->disk;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_fat_mount (disk);
+  if (! data)
+    goto fail;
+
+  struct grub_fshelp_node root = {
+    .data = data,
+    .disk = disk,
+    .attr = GRUB_FAT_ATTR_DIRECTORY,
+    .file_size = 0,
+    .file_cluster = data->root_cluster,
+    .cur_cluster_num = ~0U,
+    .cur_cluster = 0,
+#ifdef MODE_EXFAT
+    .is_contiguous = 0,
+#endif
+  };
+
+  err = grub_fshelp_find_file_lookup (name, &root, &found, lookup_file, NULL, GRUB_FSHELP_REG);
+  if (err)
+    goto fail;
+
+  file->data = found;
+  file->size = found->file_size;
+
+  return GRUB_ERR_NONE;
+
+ fail:
+
+  if (found != &root)
+    grub_free (found);
+
+  grub_free (data);
+
+  grub_dl_unref (my_mod);
+
+  return grub_errno;
+}
+
+static grub_ssize_t
+grub_fat_read (grub_file_t file, char *buf, grub_size_t len)
+{
+  return grub_fat_read_data (file->device->disk, file->data,
+                            file->read_hook, file->read_hook_data,
+                            file->offset, len, buf);
+}
+
+static grub_err_t
+grub_fat_close (grub_file_t file)
+{
+  grub_fshelp_node_t node = file->data;
+
+  grub_free (node->data);
+  grub_free (node);
+
+  grub_dl_unref (my_mod);
+
+  return grub_errno;
+}
+
+#ifdef MODE_EXFAT
+static grub_err_t
+grub_fat_label (grub_device_t device, char **label)
+{
+  struct grub_fat_dir_entry dir;
+  grub_ssize_t offset = -sizeof(dir);
+  grub_disk_t disk = device->disk;
+  struct grub_fshelp_node root = {
+    .disk = disk,
+    .attr = GRUB_FAT_ATTR_DIRECTORY,
+    .file_size = 0,
+    .cur_cluster_num = ~0U,
+    .cur_cluster = 0,
+    .is_contiguous = 0,
+  };
+
+  root.data = grub_fat_mount (disk);
+  if (! root.data)
+    return grub_errno;
+
+  root.file_cluster = root.data->root_cluster;
+
+  *label = NULL;
+
+  while (1)
+    {
+      offset += sizeof (dir);
+
+      if (grub_fat_read_data (disk, &root, 0, 0,
+                              offset, sizeof (dir), (char *) &dir)
+          != sizeof (dir))
+       break;
+
+      if (dir.entry_type == 0)
+       break;
+      if (!(dir.entry_type & 0x80))
+       continue;
+
+      /* Volume label. */
+      if (dir.entry_type == 0x83)
+       {
+         grub_size_t chc;
+         grub_uint16_t t[ARRAY_SIZE (dir.type_specific.volume_label.str)];
+         grub_size_t i;
+         *label = grub_malloc (ARRAY_SIZE (dir.type_specific.volume_label.str)
+                               * GRUB_MAX_UTF8_PER_UTF16 + 1);
+         if (!*label)
+           {
+             grub_free (root.data);
+             return grub_errno;
+           }
+         chc = dir.type_specific.volume_label.character_count;
+         if (chc > ARRAY_SIZE (dir.type_specific.volume_label.str))
+           chc = ARRAY_SIZE (dir.type_specific.volume_label.str);
+         for (i = 0; i < chc; i++)
+           t[i] = grub_le_to_cpu16 (dir.type_specific.volume_label.str[i]);
+         *grub_utf16_to_utf8 ((grub_uint8_t *) *label, t, chc) = '\0';
+       }
+    }
+
+  grub_free (root.data);
+  return grub_errno;
+}
+
+#else
+
+static grub_err_t
+grub_fat_label (grub_device_t device, char **label)
+{
+  grub_disk_t disk = device->disk;
+  grub_err_t err;
+  struct grub_fat_iterate_context ctxt;
+  struct grub_fshelp_node root = {
+    .disk = disk,
+    .attr = GRUB_FAT_ATTR_DIRECTORY,
+    .file_size = 0,
+    .cur_cluster_num = ~0U,
+    .cur_cluster = 0,
+  };
+
+  *label = 0;
+
+  grub_dl_ref (my_mod);
+
+  root.data = grub_fat_mount (disk);
+  if (! root.data)
+    goto fail;
+
+  root.file_cluster = root.data->root_cluster;
+
+  err = grub_fat_iterate_init (&ctxt);
+  if (err)
+    goto fail;
+
+  while (!(err = grub_fat_iterate_dir_next (&root, &ctxt)))
+    if ((ctxt.dir.attr & ~GRUB_FAT_ATTR_ARCHIVE) == GRUB_FAT_ATTR_VOLUME_ID)
+      {
+       *label = grub_strdup (ctxt.filename);
+       break;
+      }
+
+  grub_fat_iterate_fini (&ctxt);
+
+ fail:
+
+  grub_dl_unref (my_mod);
+
+  grub_free (root.data);
+
+  return grub_errno;
+}
+
+#endif
+
+static grub_err_t
+grub_fat_uuid (grub_device_t device, char **uuid)
+{
+  struct grub_fat_data *data;
+  grub_disk_t disk = device->disk;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_fat_mount (disk);
+  if (data)
+    {
+      char *ptr;
+      *uuid = grub_xasprintf ("%04x-%04x",
+                            (grub_uint16_t) (data->uuid >> 16),
+                            (grub_uint16_t) data->uuid);
+      for (ptr = *uuid; ptr && *ptr; ptr++)
+       *ptr = grub_toupper (*ptr);
+    }
+  else
+    *uuid = NULL;
+
+  grub_dl_unref (my_mod);
+
+  grub_free (data);
+
+  return grub_errno;
+}
+
+#ifdef GRUB_UTIL
+#ifndef MODE_EXFAT
+grub_disk_addr_t
+grub_fat_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn)
+#else
+grub_disk_addr_t
+  grub_exfat_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn)
+#endif
+{
+  grub_disk_addr_t ret;
+  struct grub_fat_data *data;
+  data = grub_fat_mount (disk);
+  if (!data)
+    return 0;
+  ret = data->cluster_sector;
+
+  *sec_per_lcn = 1ULL << data->cluster_bits;
+
+  grub_free (data);
+  return ret;
+}
+#endif
+
+static struct grub_fs grub_fat_fs =
+  {
+#ifdef MODE_EXFAT
+    .name = "exfat",
+#else
+    .name = "fat",
+#endif
+    .fs_dir = grub_fat_dir,
+    .fs_open = grub_fat_open,
+    .fs_read = grub_fat_read,
+    .fs_close = grub_fat_close,
+    .fs_label = grub_fat_label,
+    .fs_uuid = grub_fat_uuid,
+#ifdef GRUB_UTIL
+#ifdef MODE_EXFAT
+    /* ExFAT BPB is 30 larger than FAT32 one.  */
+    .reserved_first_sector = 0,
+#else
+    .reserved_first_sector = 1,
+#endif
+    .blocklist_install = 1,
+#endif
+    .next = 0
+  };
+
+#ifdef MODE_EXFAT
+GRUB_MOD_INIT(exfat)
+#else
+GRUB_MOD_INIT(fat)
+#endif
+{
+  COMPILE_TIME_ASSERT (sizeof (struct grub_fat_dir_entry) == 32);
+  grub_fs_register (&grub_fat_fs);
+  my_mod = mod;
+}
+#ifdef MODE_EXFAT
+GRUB_MOD_FINI(exfat)
+#else
+GRUB_MOD_FINI(fat)
+#endif
+{
+  grub_fs_unregister (&grub_fat_fs);
+}
+
+#ifdef MODE_EXFAT
+
+static int grub_fat_add_chunk(ventoy_img_chunk_list *chunk_list, grub_uint64_t sector, grub_uint64_t size, grub_uint32_t log_sector_size)
+{
+    ventoy_img_chunk *last_chunk;
+    ventoy_img_chunk *new_chunk;
+    
+    if (chunk_list->cur_chunk == 0)
+    {
+        chunk_list->chunk[0].img_start_sector = 0;
+        chunk_list->chunk[0].img_end_sector = (size >> 11) - 1;
+        chunk_list->chunk[0].disk_start_sector = sector;
+        chunk_list->chunk[0].disk_end_sector = sector + (size >> log_sector_size) - 1;
+        chunk_list->cur_chunk = 1;
+        return 0;
+    }
+
+    last_chunk = chunk_list->chunk + chunk_list->cur_chunk - 1;
+    if (last_chunk->disk_end_sector + 1 == sector)
+    {
+        last_chunk->img_end_sector  += (size >> 11);
+        last_chunk->disk_end_sector += (size >> log_sector_size);
+        return 0;
+    }
+
+    if (chunk_list->cur_chunk == chunk_list->max_chunk)
+    {
+        new_chunk = grub_realloc(chunk_list->chunk, chunk_list->max_chunk * 2 * sizeof(ventoy_img_chunk));
+        if (NULL == new_chunk)
+        {
+            return -1;
+        }
+        chunk_list->chunk = new_chunk;
+        chunk_list->max_chunk *= 2;
+
+        /* issue: update last_chunk */
+        last_chunk = chunk_list->chunk + chunk_list->cur_chunk - 1;
+    }
+
+    new_chunk = chunk_list->chunk + chunk_list->cur_chunk;
+    new_chunk->img_start_sector = last_chunk->img_end_sector + 1;
+    new_chunk->img_end_sector = new_chunk->img_start_sector + (size >> 11) - 1;
+    new_chunk->disk_start_sector = sector;
+    new_chunk->disk_end_sector = sector + (size >> log_sector_size) - 1;
+
+    chunk_list->cur_chunk++;
+
+    return 0;
+}
+
+int grub_fat_get_file_chunk(grub_uint64_t part_start, grub_file_t file, ventoy_img_chunk_list *chunk_list)
+{
+    grub_size_t size;
+    grub_uint32_t i;
+    grub_uint32_t logical_cluster;
+    unsigned logical_cluster_bits;
+    unsigned long sector;
+    grub_fshelp_node_t node;
+    grub_disk_t disk;
+    grub_uint64_t len;
+
+    disk = file->device->disk;
+    node = file->data;
+    len = file->size;
+
+    if (node->is_contiguous)
+    {
+        /* Read the data here.  */
+        sector = (node->data->cluster_sector + ((node->file_cluster - 2) << node->data->cluster_bits));
+
+        chunk_list->chunk[0].img_start_sector = 0;
+        chunk_list->chunk[0].img_end_sector = (file->size >> 11) - 1;
+        chunk_list->chunk[0].disk_start_sector = sector;
+        chunk_list->chunk[0].disk_end_sector = sector + (file->size >> disk->log_sector_size) - 1;
+        chunk_list->cur_chunk = 1;
+
+        goto END;
+    }
+
+    /* Calculate the logical cluster number and offset.  */
+    logical_cluster = 0;
+    logical_cluster_bits = (node->data->cluster_bits + GRUB_DISK_SECTOR_BITS);
+
+    if (logical_cluster < node->cur_cluster_num)
+    {
+        node->cur_cluster_num = 0;
+        node->cur_cluster = node->file_cluster;
+    }
+
+    while (len)
+    {
+        while (logical_cluster > node->cur_cluster_num)
+        {
+            /* Find next cluster.  */
+            grub_uint32_t next_cluster;
+            grub_uint32_t fat_offset;
+
+            switch (node->data->fat_size)
+            {
+                case 32:
+                    fat_offset = node->cur_cluster << 2;
+                    break;
+                case 16:
+                    fat_offset = node->cur_cluster << 1;
+                    break;
+                default:
+                    /* case 12: */
+                    fat_offset = node->cur_cluster + (node->cur_cluster >> 1);
+                    break;
+            }
+
+            /* Read the FAT.  */      
+            if (grub_disk_read (disk, node->data->fat_sector, fat_offset,
+                     (node->data->fat_size + 7) >> 3,
+                     (char *) &next_cluster))
+            {
+                return -1;                
+            }
+
+            next_cluster = grub_le_to_cpu32 (next_cluster);
+            switch (node->data->fat_size)
+            {
+                case 16:
+                    next_cluster &= 0xFFFF;
+                    break;
+                case 12:
+                    if (node->cur_cluster & 1)
+                    next_cluster >>= 4;
+
+                    next_cluster &= 0x0FFF;
+                    break;
+            }
+
+            grub_dprintf ("fat", "fat_size=%d, next_cluster=%u\n", node->data->fat_size, next_cluster);
+
+            /* Check the end.  */
+            if (next_cluster >= node->data->cluster_eof_mark)
+            {
+                return 0;
+            }
+
+            if (next_cluster < 2 || next_cluster >= node->data->num_clusters)
+            {
+                grub_error (GRUB_ERR_BAD_FS, "invalid cluster %u", next_cluster);
+                return -1;
+            }
+
+            node->cur_cluster = next_cluster;
+            node->cur_cluster_num++;
+        }
+
+      /* Read the data here.  */
+      sector = (node->data->cluster_sector
+               + ((node->cur_cluster - 2)
+                  << node->data->cluster_bits));
+      size = (1 << logical_cluster_bits);
+      if (size > len)
+           size = len;
+
+    grub_fat_add_chunk(chunk_list, sector, size, disk->log_sector_size);
+
+      len -= size;
+      logical_cluster++;
+    }
+
+END:
+
+    for (i = 0; i < chunk_list->cur_chunk; i++)
+    {
+        chunk_list->chunk[i].disk_start_sector += part_start;
+        chunk_list->chunk[i].disk_end_sector += part_start;
+    }
+
+    return 0;
+}
+
+#endif
+
diff --git a/GRUB2/grub-2.04/grub-core/fs/iso9660.c b/GRUB2/grub-2.04/grub-core/fs/iso9660.c
new file mode 100644 (file)
index 0000000..f0e7dcf
--- /dev/null
@@ -0,0 +1,1149 @@
+/* iso9660.c - iso9660 implementation with extensions:
+   SUSP, Rock Ridge.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2004,2005,2006,2007,2008,2009,2010  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/err.h>
+#include <grub/file.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/disk.h>
+#include <grub/dl.h>
+#include <grub/types.h>
+#include <grub/fshelp.h>
+#include <grub/charset.h>
+#include <grub/datetime.h>
+#include <grub/ventoy.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_uint64_t g_ventoy_last_read_pos = 0;
+static grub_uint64_t g_ventoy_last_read_offset = 0;
+static grub_uint64_t g_ventoy_last_read_dirent_pos = 0;
+static grub_uint64_t g_ventoy_last_read_dirent_offset = 0;
+static grub_uint64_t g_ventoy_last_file_dirent_pos = 0;
+static grub_uint64_t g_ventoy_last_file_dirent_offset = 0;
+
+#define GRUB_ISO9660_FSTYPE_DIR                0040000
+#define GRUB_ISO9660_FSTYPE_REG                0100000
+#define GRUB_ISO9660_FSTYPE_SYMLINK    0120000
+#define GRUB_ISO9660_FSTYPE_MASK       0170000
+
+#define GRUB_ISO9660_LOG2_BLKSZ                2
+#define GRUB_ISO9660_BLKSZ             2048
+
+#define GRUB_ISO9660_RR_DOT            2
+#define GRUB_ISO9660_RR_DOTDOT         4
+
+#define GRUB_ISO9660_VOLDESC_BOOT      0
+#define GRUB_ISO9660_VOLDESC_PRIMARY   1
+#define GRUB_ISO9660_VOLDESC_SUPP      2
+#define GRUB_ISO9660_VOLDESC_PART      3
+#define GRUB_ISO9660_VOLDESC_END       255
+
+/* The head of a volume descriptor.  */
+struct grub_iso9660_voldesc
+{
+  grub_uint8_t type;
+  grub_uint8_t magic[5];
+  grub_uint8_t version;
+} GRUB_PACKED;
+
+struct grub_iso9660_date2
+{
+  grub_uint8_t year;
+  grub_uint8_t month;
+  grub_uint8_t day;
+  grub_uint8_t hour;
+  grub_uint8_t minute;
+  grub_uint8_t second;
+  grub_uint8_t offset;
+} GRUB_PACKED;
+
+/* A directory entry.  */
+struct grub_iso9660_dir
+{
+  grub_uint8_t len;
+  grub_uint8_t ext_sectors;
+  grub_uint32_t first_sector;
+  grub_uint32_t first_sector_be;
+  grub_uint32_t size;
+  grub_uint32_t size_be;
+  struct grub_iso9660_date2 mtime;
+  grub_uint8_t flags;
+  grub_uint8_t unused2[6];
+#define MAX_NAMELEN 255
+  grub_uint8_t namelen;
+} GRUB_PACKED;
+
+struct grub_iso9660_date
+{
+  grub_uint8_t year[4];
+  grub_uint8_t month[2];
+  grub_uint8_t day[2];
+  grub_uint8_t hour[2];
+  grub_uint8_t minute[2];
+  grub_uint8_t second[2];
+  grub_uint8_t hundredth[2];
+  grub_uint8_t offset;
+} GRUB_PACKED;
+
+/* The primary volume descriptor.  Only little endian is used.  */
+struct grub_iso9660_primary_voldesc
+{
+  struct grub_iso9660_voldesc voldesc;
+  grub_uint8_t unused1[33];
+  grub_uint8_t volname[32];
+  grub_uint8_t unused2[16];
+  grub_uint8_t escape[32];
+  grub_uint8_t unused3[12];
+  grub_uint32_t path_table_size;
+  grub_uint8_t unused4[4];
+  grub_uint32_t path_table;
+  grub_uint8_t unused5[12];
+  struct grub_iso9660_dir rootdir;
+  grub_uint8_t unused6[624];
+  struct grub_iso9660_date created;
+  struct grub_iso9660_date modified;
+} GRUB_PACKED;
+
+/* A single entry in the path table.  */
+struct grub_iso9660_path
+{
+  grub_uint8_t len;
+  grub_uint8_t sectors;
+  grub_uint32_t first_sector;
+  grub_uint16_t parentdir;
+  grub_uint8_t name[0];
+} GRUB_PACKED;
+
+/* An entry in the System Usage area of the directory entry.  */
+struct grub_iso9660_susp_entry
+{
+  grub_uint8_t sig[2];
+  grub_uint8_t len;
+  grub_uint8_t version;
+  grub_uint8_t data[0];
+} GRUB_PACKED;
+
+/* The CE entry.  This is used to describe the next block where data
+   can be found.  */
+struct grub_iso9660_susp_ce
+{
+  struct grub_iso9660_susp_entry entry;
+  grub_uint32_t blk;
+  grub_uint32_t blk_be;
+  grub_uint32_t off;
+  grub_uint32_t off_be;
+  grub_uint32_t len;
+  grub_uint32_t len_be;
+} GRUB_PACKED;
+
+struct grub_iso9660_data
+{
+  struct grub_iso9660_primary_voldesc voldesc;
+  grub_disk_t disk;
+  int rockridge;
+  int susp_skip;
+  int joliet;
+  struct grub_fshelp_node *node;
+};
+
+struct grub_fshelp_node
+{
+  struct grub_iso9660_data *data;
+  grub_size_t have_dirents, alloc_dirents;
+  int have_symlink;
+  struct grub_iso9660_dir dirents[8];
+  char symlink[0];
+};
+
+enum
+  {
+    FLAG_TYPE_PLAIN = 0,
+    FLAG_TYPE_DIR = 2,
+    FLAG_TYPE = 3,
+    FLAG_MORE_EXTENTS = 0x80
+  };
+
+static grub_dl_t my_mod;
+\f
+
+static grub_err_t
+iso9660_to_unixtime (const struct grub_iso9660_date *i, grub_int32_t *nix)
+{
+  struct grub_datetime datetime;
+  
+  if (! i->year[0] && ! i->year[1]
+      && ! i->year[2] && ! i->year[3]
+      && ! i->month[0] && ! i->month[1]
+      && ! i->day[0] && ! i->day[1]
+      && ! i->hour[0] && ! i->hour[1]
+      && ! i->minute[0] && ! i->minute[1]
+      && ! i->second[0] && ! i->second[1]
+      && ! i->hundredth[0] && ! i->hundredth[1])
+    return grub_error (GRUB_ERR_BAD_NUMBER, "empty date");
+  datetime.year = (i->year[0] - '0') * 1000 + (i->year[1] - '0') * 100
+    + (i->year[2] - '0') * 10 + (i->year[3] - '0');
+  datetime.month = (i->month[0] - '0') * 10 + (i->month[1] - '0');
+  datetime.day = (i->day[0] - '0') * 10 + (i->day[1] - '0');
+  datetime.hour = (i->hour[0] - '0') * 10 + (i->hour[1] - '0');
+  datetime.minute = (i->minute[0] - '0') * 10 + (i->minute[1] - '0');
+  datetime.second = (i->second[0] - '0') * 10 + (i->second[1] - '0');
+  
+  if (!grub_datetime2unixtime (&datetime, nix))
+    return grub_error (GRUB_ERR_BAD_NUMBER, "incorrect date");
+  *nix -= i->offset * 60 * 15;
+  return GRUB_ERR_NONE;
+}
+
+static int
+iso9660_to_unixtime2 (const struct grub_iso9660_date2 *i, grub_int32_t *nix)
+{
+  struct grub_datetime datetime;
+
+  datetime.year = i->year + 1900;
+  datetime.month = i->month;
+  datetime.day = i->day;
+  datetime.hour = i->hour;
+  datetime.minute = i->minute;
+  datetime.second = i->second;
+  
+  if (!grub_datetime2unixtime (&datetime, nix))
+    return 0;
+  *nix -= i->offset * 60 * 15;
+  return 1;
+}
+
+static grub_err_t
+read_node (grub_fshelp_node_t node, grub_off_t off, grub_size_t len, char *buf)
+{
+  grub_size_t i = 0;
+
+  while (len > 0)
+    {
+      grub_size_t toread;
+      grub_err_t err;
+      while (i < node->have_dirents
+            && off >= grub_le_to_cpu32 (node->dirents[i].size))
+       {
+         off -= grub_le_to_cpu32 (node->dirents[i].size);
+         i++;
+       }
+      if (i == node->have_dirents)
+       return grub_error (GRUB_ERR_OUT_OF_RANGE, "read out of range");
+      toread = grub_le_to_cpu32 (node->dirents[i].size);
+      if (toread > len)
+       toread = len;
+      g_ventoy_last_read_pos = ((grub_disk_addr_t) grub_le_to_cpu32 (node->dirents[i].first_sector)) << GRUB_ISO9660_LOG2_BLKSZ;
+      g_ventoy_last_read_offset = off;
+      err = grub_disk_read (node->data->disk, g_ventoy_last_read_pos, off, toread, buf);
+      if (err)
+       return err;
+      len -= toread;
+      off += toread;
+      buf += toread;
+    }
+  return GRUB_ERR_NONE;
+}
+
+/* Iterate over the susp entries, starting with block SUA_BLOCK on the
+   offset SUA_POS with a size of SUA_SIZE bytes.  Hook is called for
+   every entry.  */
+static grub_err_t
+grub_iso9660_susp_iterate (grub_fshelp_node_t node, grub_off_t off,
+                          grub_ssize_t sua_size,
+                          grub_err_t (*hook)
+                          (struct grub_iso9660_susp_entry *entry, void *hook_arg),
+                          void *hook_arg)
+{
+  char *sua;
+  struct grub_iso9660_susp_entry *entry;
+  grub_err_t err;
+
+  if (sua_size <= 0)
+    return GRUB_ERR_NONE;
+
+  sua = grub_malloc (sua_size);
+  if (!sua)
+    return grub_errno;
+
+  /* Load a part of the System Usage Area.  */
+  err = read_node (node, off, sua_size, sua);
+  if (err)
+    return err;
+
+  for (entry = (struct grub_iso9660_susp_entry *) sua; (char *) entry < (char *) sua + sua_size - 1 && entry->len > 0;
+       entry = (struct grub_iso9660_susp_entry *)
+        ((char *) entry + entry->len))
+    {
+      /* The last entry.  */
+      if (grub_strncmp ((char *) entry->sig, "ST", 2) == 0)
+       break;
+
+      /* Additional entries are stored elsewhere.  */
+      if (grub_strncmp ((char *) entry->sig, "CE", 2) == 0)
+       {
+         struct grub_iso9660_susp_ce *ce;
+         grub_disk_addr_t ce_block;
+
+         ce = (struct grub_iso9660_susp_ce *) entry;
+         sua_size = grub_le_to_cpu32 (ce->len);
+         off = grub_le_to_cpu32 (ce->off);
+         ce_block = grub_le_to_cpu32 (ce->blk) << GRUB_ISO9660_LOG2_BLKSZ;
+
+         grub_free (sua);
+         sua = grub_malloc (sua_size);
+         if (!sua)
+           return grub_errno;
+
+         /* Load a part of the System Usage Area.  */
+         err = grub_disk_read (node->data->disk, ce_block, off,
+                               sua_size, sua);
+         if (err)
+           return err;
+
+         entry = (struct grub_iso9660_susp_entry *) sua;
+       }
+
+      if (hook (entry, hook_arg))
+       {
+         grub_free (sua);
+         return 0;
+       }
+    }
+
+  grub_free (sua);
+  return 0;
+}
+
+static char *
+grub_iso9660_convert_string (grub_uint8_t *us, int len)
+{
+  char *p;
+  int i;
+  grub_uint16_t t[MAX_NAMELEN / 2 + 1];
+
+  p = grub_malloc (len * GRUB_MAX_UTF8_PER_UTF16 + 1);
+  if (! p)
+    return NULL;
+
+  for (i=0; i<len; i++)
+    t[i] = grub_be_to_cpu16 (grub_get_unaligned16 (us + 2 * i));
+
+  *grub_utf16_to_utf8 ((grub_uint8_t *) p, t, len) = '\0';
+
+  return p;
+}
+
+static grub_err_t
+susp_iterate_set_rockridge (struct grub_iso9660_susp_entry *susp_entry,
+                           void *_data)
+{
+  struct grub_iso9660_data *data = _data;
+  /* The "ER" entry is used to detect extensions.  The
+     `IEEE_P1285' extension means Rock ridge.  */
+  if (grub_strncmp ((char *) susp_entry->sig, "ER", 2) == 0)
+    {
+      data->rockridge = 1;
+      return 1;
+    }
+  return 0;
+}
+
+static grub_err_t
+set_rockridge (struct grub_iso9660_data *data)
+{
+  int sua_pos;
+  int sua_size;
+  char *sua;
+  struct grub_iso9660_dir rootdir;
+  struct grub_iso9660_susp_entry *entry;
+
+  data->rockridge = 0;
+
+  /* Read the system use area and test it to see if SUSP is
+     supported.  */
+  if (grub_disk_read (data->disk,
+                     (grub_le_to_cpu32 (data->voldesc.rootdir.first_sector)
+                      << GRUB_ISO9660_LOG2_BLKSZ), 0,
+                     sizeof (rootdir), (char *) &rootdir))
+    return grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem");
+
+  sua_pos = (sizeof (rootdir) + rootdir.namelen
+            + (rootdir.namelen % 2) - 1);
+  sua_size = rootdir.len - sua_pos;
+
+  if (!sua_size)
+    return GRUB_ERR_NONE;
+
+  sua = grub_malloc (sua_size);
+  if (! sua)
+    return grub_errno;
+
+  if (grub_disk_read (data->disk,
+                     (grub_le_to_cpu32 (data->voldesc.rootdir.first_sector)
+                      << GRUB_ISO9660_LOG2_BLKSZ), sua_pos,
+                     sua_size, sua))
+    {
+      grub_free (sua);
+      return grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem");
+    }
+
+  entry = (struct grub_iso9660_susp_entry *) sua;
+
+  /* Test if the SUSP protocol is used on this filesystem.  */
+  if (grub_strncmp ((char *) entry->sig, "SP", 2) == 0)
+    {
+      struct grub_fshelp_node rootnode;
+
+      rootnode.data = data;
+      rootnode.alloc_dirents = ARRAY_SIZE (rootnode.dirents);
+      rootnode.have_dirents = 1;
+      rootnode.have_symlink = 0;
+      rootnode.dirents[0] = data->voldesc.rootdir;
+
+      /* The 2nd data byte stored how many bytes are skipped every time
+        to get to the SUA (System Usage Area).  */
+      data->susp_skip = entry->data[2];
+      entry = (struct grub_iso9660_susp_entry *) ((char *) entry + entry->len);
+
+      /* Iterate over the entries in the SUA area to detect
+        extensions.  */
+      if (grub_iso9660_susp_iterate (&rootnode,
+                                    sua_pos, sua_size, susp_iterate_set_rockridge,
+                                    data))
+       {
+         grub_free (sua);
+         return grub_errno;
+       }
+    }
+  grub_free (sua);
+  return GRUB_ERR_NONE;
+}
+
+static struct grub_iso9660_data *
+grub_iso9660_mount (grub_disk_t disk)
+{
+  struct grub_iso9660_data *data = 0;
+  struct grub_iso9660_primary_voldesc voldesc;
+  int block;
+
+  data = grub_zalloc (sizeof (struct grub_iso9660_data));
+  if (! data)
+    return 0;
+
+  data->disk = disk;
+
+  block = 16;
+  do
+    {
+      int copy_voldesc = 0;
+
+      /* Read the superblock.  */
+      if (grub_disk_read (disk, block << GRUB_ISO9660_LOG2_BLKSZ, 0,
+                         sizeof (struct grub_iso9660_primary_voldesc),
+                         (char *) &voldesc))
+        {
+          grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem");
+          goto fail;
+        }
+
+      if (grub_strncmp ((char *) voldesc.voldesc.magic, "CD001", 5) != 0)
+        {
+          grub_error (GRUB_ERR_BAD_FS, "not a ISO9660 filesystem");
+          goto fail;
+        }
+
+      if (voldesc.voldesc.type == GRUB_ISO9660_VOLDESC_PRIMARY)
+       copy_voldesc = 1;
+      else if (!data->rockridge
+              && (voldesc.voldesc.type == GRUB_ISO9660_VOLDESC_SUPP)
+              && (voldesc.escape[0] == 0x25) && (voldesc.escape[1] == 0x2f)
+              &&
+               ((voldesc.escape[2] == 0x40) || /* UCS-2 Level 1.  */
+                (voldesc.escape[2] == 0x43) ||  /* UCS-2 Level 2.  */
+                (voldesc.escape[2] == 0x45)))  /* UCS-2 Level 3.  */
+        {
+          copy_voldesc = 1;
+          data->joliet = 1;
+        }
+
+      if (copy_voldesc)
+       {
+         grub_memcpy((char *) &data->voldesc, (char *) &voldesc,
+                     sizeof (struct grub_iso9660_primary_voldesc));
+         if (set_rockridge (data))
+           goto fail;
+       }
+
+      block++;
+    } while (voldesc.voldesc.type != GRUB_ISO9660_VOLDESC_END);
+
+  return data;
+
+ fail:
+  grub_free (data);
+  return 0;
+}
+
+
+static char *
+grub_iso9660_read_symlink (grub_fshelp_node_t node)
+{
+  return node->have_symlink 
+    ? grub_strdup (node->symlink
+                  + (node->have_dirents) * sizeof (node->dirents[0])
+                  - sizeof (node->dirents)) : grub_strdup ("");
+}
+
+static grub_off_t
+get_node_size (grub_fshelp_node_t node)
+{
+  grub_off_t ret = 0;
+  grub_size_t i;
+
+  for (i = 0; i < node->have_dirents; i++)
+    ret += grub_le_to_cpu32 (node->dirents[i].size);
+  return ret;
+}
+
+struct iterate_dir_ctx
+{
+  char *filename;
+  int filename_alloc;
+  enum grub_fshelp_filetype type;
+  char *symlink;
+  int was_continue;
+};
+
+  /* Extend the symlink.  */
+static void
+add_part (struct iterate_dir_ctx *ctx,
+         const char *part,
+         int len2)
+{
+  int size = ctx->symlink ? grub_strlen (ctx->symlink) : 0;
+
+  ctx->symlink = grub_realloc (ctx->symlink, size + len2 + 1);
+  if (! ctx->symlink)
+    return;
+
+  grub_memcpy (ctx->symlink + size, part, len2);
+  ctx->symlink[size + len2] = 0;  
+}
+
+static grub_err_t
+susp_iterate_dir (struct grub_iso9660_susp_entry *entry,
+                 void *_ctx)
+{
+  struct iterate_dir_ctx *ctx = _ctx;
+
+  /* The filename in the rock ridge entry.  */
+  if (grub_strncmp ("NM", (char *) entry->sig, 2) == 0)
+    {
+      /* The flags are stored at the data position 0, here the
+        filename type is stored.  */
+      /* FIXME: Fix this slightly improper cast.  */
+      if (entry->data[0] & GRUB_ISO9660_RR_DOT)
+       ctx->filename = (char *) ".";
+      else if (entry->data[0] & GRUB_ISO9660_RR_DOTDOT)
+       ctx->filename = (char *) "..";
+      else if (entry->len >= 5)
+       {
+         grub_size_t off = 0, csize = 1;
+         char *old;
+         csize = entry->len - 5;
+         old = ctx->filename;
+         if (ctx->filename_alloc)
+           {
+             off = grub_strlen (ctx->filename);
+             ctx->filename = grub_realloc (ctx->filename, csize + off + 1);
+           }
+         else
+           {
+             off = 0;
+             ctx->filename = grub_zalloc (csize + 1);
+           }
+         if (!ctx->filename)
+           {
+             ctx->filename = old;
+             return grub_errno;
+           }
+         ctx->filename_alloc = 1;
+         grub_memcpy (ctx->filename + off, (char *) &entry->data[1], csize);
+         ctx->filename[off + csize] = '\0';
+       }
+    }
+  /* The mode information (st_mode).  */
+  else if (grub_strncmp ((char *) entry->sig, "PX", 2) == 0)
+    {
+      /* At position 0 of the PX record the st_mode information is
+        stored (little-endian).  */
+      grub_uint32_t mode = ((entry->data[0] + (entry->data[1] << 8))
+                           & GRUB_ISO9660_FSTYPE_MASK);
+
+      switch (mode)
+       {
+       case GRUB_ISO9660_FSTYPE_DIR:
+         ctx->type = GRUB_FSHELP_DIR;
+         break;
+       case GRUB_ISO9660_FSTYPE_REG:
+         ctx->type = GRUB_FSHELP_REG;
+         break;
+       case GRUB_ISO9660_FSTYPE_SYMLINK:
+         ctx->type = GRUB_FSHELP_SYMLINK;
+         break;
+       default:
+         ctx->type = GRUB_FSHELP_UNKNOWN;
+       }
+    }
+  else if (grub_strncmp ("SL", (char *) entry->sig, 2) == 0)
+    {
+      unsigned int pos = 1;
+
+      /* The symlink is not stored as a POSIX symlink, translate it.  */
+      while (pos + sizeof (*entry) < entry->len)
+       {
+         /* The current position is the `Component Flag'.  */
+         switch (entry->data[pos] & 30)
+           {
+           case 0:
+             {
+               /* The data on pos + 2 is the actual data, pos + 1
+                  is the length.  Both are part of the `Component
+                  Record'.  */
+               if (ctx->symlink && !ctx->was_continue)
+                 add_part (ctx, "/", 1);
+               add_part (ctx, (char *) &entry->data[pos + 2],
+                         entry->data[pos + 1]);
+               ctx->was_continue = (entry->data[pos] & 1);
+               break;
+             }
+
+           case 2:
+             add_part (ctx, "./", 2);
+             break;
+
+           case 4:
+             add_part (ctx, "../", 3);
+             break;
+
+           case 8:
+             add_part (ctx, "/", 1);
+             break;
+           }
+         /* In pos + 1 the length of the `Component Record' is
+            stored.  */
+         pos += entry->data[pos + 1] + 2;
+       }
+
+      /* Check if `grub_realloc' failed.  */
+      if (grub_errno)
+       return grub_errno;
+    }
+
+  return 0;
+}
+
+static int
+grub_iso9660_iterate_dir (grub_fshelp_node_t dir,
+                         grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
+{
+  struct grub_iso9660_dir dirent;
+  grub_off_t offset = 0;
+  grub_off_t len;
+  struct iterate_dir_ctx ctx;
+
+  len = get_node_size (dir);
+
+  for (; offset < len; offset += dirent.len)
+    {
+      ctx.symlink = 0;
+      ctx.was_continue = 0;
+
+      if (read_node (dir, offset, sizeof (dirent), (char *) &dirent))
+       return 0;
+
+      if ((dirent.flags & FLAG_TYPE) != FLAG_TYPE_DIR) {
+        g_ventoy_last_read_dirent_pos = g_ventoy_last_read_pos;
+        g_ventoy_last_read_dirent_offset = g_ventoy_last_read_offset;
+      }
+
+      /* The end of the block, skip to the next one.  */
+      if (!dirent.len)
+       {
+         offset = (offset / GRUB_ISO9660_BLKSZ + 1) * GRUB_ISO9660_BLKSZ;
+         continue;
+       }
+
+      {
+       char name[MAX_NAMELEN + 1];
+       int nameoffset = offset + sizeof (dirent);
+       struct grub_fshelp_node *node;
+       int sua_off = (sizeof (dirent) + dirent.namelen + 1
+                      - (dirent.namelen % 2));
+       int sua_size = dirent.len - sua_off;
+
+       sua_off += offset + dir->data->susp_skip;
+
+       ctx.filename = 0;
+       ctx.filename_alloc = 0;
+       ctx.type = GRUB_FSHELP_UNKNOWN;
+
+       if (dir->data->rockridge
+           && grub_iso9660_susp_iterate (dir, sua_off, sua_size,
+                                         susp_iterate_dir, &ctx))
+         return 0;
+
+       /* Read the name.  */
+       if (read_node (dir, nameoffset, dirent.namelen, (char *) name))
+         return 0;
+
+       node = grub_malloc (sizeof (struct grub_fshelp_node));
+       if (!node)
+         return 0;
+
+       node->alloc_dirents = ARRAY_SIZE (node->dirents);
+       node->have_dirents = 1;
+
+       /* Setup a new node.  */
+       node->data = dir->data;
+       node->have_symlink = 0;
+
+       /* If the filetype was not stored using rockridge, use
+          whatever is stored in the iso9660 filesystem.  */
+       if (ctx.type == GRUB_FSHELP_UNKNOWN)
+         {
+           if ((dirent.flags & FLAG_TYPE) == FLAG_TYPE_DIR)
+             ctx.type = GRUB_FSHELP_DIR;
+           else
+             ctx.type = GRUB_FSHELP_REG;
+         }
+
+       /* . and .. */
+       if (!ctx.filename && dirent.namelen == 1 && name[0] == 0)
+         ctx.filename = (char *) ".";
+
+       if (!ctx.filename && dirent.namelen == 1 && name[0] == 1)
+         ctx.filename = (char *) "..";
+
+       /* The filename was not stored in a rock ridge entry.  Read it
+          from the iso9660 filesystem.  */
+       if (!dir->data->joliet && !ctx.filename)
+         {
+           char *ptr;
+           name[dirent.namelen] = '\0';
+           ctx.filename = grub_strrchr (name, ';');
+           if (ctx.filename)
+             *ctx.filename = '\0';
+           /* ISO9660 names are not case-preserving.  */
+           ctx.type |= GRUB_FSHELP_CASE_INSENSITIVE;
+           for (ptr = name; *ptr; ptr++)
+             *ptr = grub_tolower (*ptr);
+           if (ptr != name && *(ptr - 1) == '.')
+             *(ptr - 1) = 0;
+           ctx.filename = name;
+         }
+
+        if (dir->data->joliet && !ctx.filename)
+          {
+            char *semicolon;
+
+            ctx.filename = grub_iso9660_convert_string
+                  ((grub_uint8_t *) name, dirent.namelen >> 1);
+
+           semicolon = grub_strrchr (ctx.filename, ';');
+           if (semicolon)
+             *semicolon = '\0';
+
+            ctx.filename_alloc = 1;
+          }
+
+       node->dirents[0] = dirent;
+       while (dirent.flags & FLAG_MORE_EXTENTS)
+         {
+           offset += dirent.len;
+           if (read_node (dir, offset, sizeof (dirent), (char *) &dirent))
+             {
+               if (ctx.filename_alloc)
+                 grub_free (ctx.filename);
+               grub_free (node);
+               return 0;
+             }
+           if (node->have_dirents >= node->alloc_dirents)
+             {
+               struct grub_fshelp_node *new_node;
+               node->alloc_dirents *= 2;
+               new_node = grub_realloc (node, 
+                                        sizeof (struct grub_fshelp_node)
+                                        + ((node->alloc_dirents
+                                            - ARRAY_SIZE (node->dirents))
+                                           * sizeof (node->dirents[0])));
+               if (!new_node)
+                 {
+                   if (ctx.filename_alloc)
+                     grub_free (ctx.filename);
+                   grub_free (node);
+                   return 0;
+                 }
+               node = new_node;
+             }
+           node->dirents[node->have_dirents++] = dirent;
+         }
+       if (ctx.symlink)
+         {
+           if ((node->alloc_dirents - node->have_dirents)
+               * sizeof (node->dirents[0]) < grub_strlen (ctx.symlink) + 1)
+             {
+               struct grub_fshelp_node *new_node;
+               new_node = grub_realloc (node,
+                                        sizeof (struct grub_fshelp_node)
+                                        + ((node->alloc_dirents
+                                            - ARRAY_SIZE (node->dirents))
+                                           * sizeof (node->dirents[0]))
+                                        + grub_strlen (ctx.symlink) + 1);
+               if (!new_node)
+                 {
+                   if (ctx.filename_alloc)
+                     grub_free (ctx.filename);
+                   grub_free (node);
+                   return 0;
+                 }
+               node = new_node;
+             }
+           node->have_symlink = 1;
+           grub_strcpy (node->symlink
+                        + node->have_dirents * sizeof (node->dirents[0])
+                        - sizeof (node->dirents), ctx.symlink);
+           grub_free (ctx.symlink);
+           ctx.symlink = 0;
+           ctx.was_continue = 0;
+         }
+       if (hook (ctx.filename, ctx.type, node, hook_data))
+         {
+        g_ventoy_last_file_dirent_pos = g_ventoy_last_read_dirent_pos;
+        g_ventoy_last_file_dirent_offset = g_ventoy_last_read_dirent_offset;
+           if (ctx.filename_alloc)
+             grub_free (ctx.filename);
+           return 1;
+         }
+       if (ctx.filename_alloc)
+         grub_free (ctx.filename);
+      }
+    }
+
+  return 0;
+}
+
+
+\f
+/* Context for grub_iso9660_dir.  */
+struct grub_iso9660_dir_ctx
+{
+  grub_fs_dir_hook_t hook;
+  void *hook_data;
+};
+
+/* Helper for grub_iso9660_dir.  */
+static int
+grub_iso9660_dir_iter (const char *filename,
+                      enum grub_fshelp_filetype filetype,
+                      grub_fshelp_node_t node, void *data)
+{
+  struct grub_iso9660_dir_ctx *ctx = data;
+  struct grub_dirhook_info info;
+
+  grub_memset (&info, 0, sizeof (info));
+  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
+  info.mtimeset = !!iso9660_to_unixtime2 (&node->dirents[0].mtime, &info.mtime);
+
+  grub_free (node);
+  return ctx->hook (filename, &info, ctx->hook_data);
+}
+
+static grub_err_t
+grub_iso9660_dir (grub_device_t device, const char *path,
+                 grub_fs_dir_hook_t hook, void *hook_data)
+{
+  struct grub_iso9660_dir_ctx ctx = { hook, hook_data };
+  struct grub_iso9660_data *data = 0;
+  struct grub_fshelp_node rootnode;
+  struct grub_fshelp_node *foundnode;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_iso9660_mount (device->disk);
+  if (! data)
+    goto fail;
+
+  rootnode.data = data;
+  rootnode.alloc_dirents = 0;
+  rootnode.have_dirents = 1;
+  rootnode.have_symlink = 0;
+  rootnode.dirents[0] = data->voldesc.rootdir;
+
+  /* Use the fshelp function to traverse the path.  */
+  if (grub_fshelp_find_file (path, &rootnode,
+                            &foundnode,
+                            grub_iso9660_iterate_dir,
+                            grub_iso9660_read_symlink,
+                            GRUB_FSHELP_DIR))
+    goto fail;
+
+  /* List the files in the directory.  */
+  grub_iso9660_iterate_dir (foundnode, grub_iso9660_dir_iter, &ctx);
+
+  if (foundnode != &rootnode)
+    grub_free (foundnode);
+
+ fail:
+  grub_free (data);
+
+  grub_dl_unref (my_mod);
+
+  return grub_errno;
+}
+
+
+/* Open a file named NAME and initialize FILE.  */
+static grub_err_t
+grub_iso9660_open (struct grub_file *file, const char *name)
+{
+  struct grub_iso9660_data *data;
+  struct grub_fshelp_node rootnode;
+  struct grub_fshelp_node *foundnode;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_iso9660_mount (file->device->disk);
+  if (!data)
+    goto fail;
+
+  rootnode.data = data;
+  rootnode.alloc_dirents = 0;
+  rootnode.have_dirents = 1;
+  rootnode.have_symlink = 0;
+  rootnode.dirents[0] = data->voldesc.rootdir;
+
+  /* Use the fshelp function to traverse the path.  */
+  if (grub_fshelp_find_file (name, &rootnode,
+                            &foundnode,
+                            grub_iso9660_iterate_dir,
+                            grub_iso9660_read_symlink,
+                            GRUB_FSHELP_REG))
+    goto fail;
+
+  data->node = foundnode;
+  file->data = data;
+  file->size = get_node_size (foundnode);
+  file->offset = 0;
+
+  return 0;
+
+ fail:
+  grub_dl_unref (my_mod);
+
+  grub_free (data);
+
+  return grub_errno;
+}
+
+
+static grub_ssize_t
+grub_iso9660_read (grub_file_t file, char *buf, grub_size_t len)
+{
+  struct grub_iso9660_data *data =
+    (struct grub_iso9660_data *) file->data;
+  grub_err_t err;
+
+  /* XXX: The file is stored in as a single extent.  */
+  data->disk->read_hook = file->read_hook;
+  data->disk->read_hook_data = file->read_hook_data;
+  err = read_node (data->node, file->offset, len, buf);
+  data->disk->read_hook = NULL;
+
+  if (err || grub_errno)
+    return -1;
+
+  return len;
+}
+
+
+static grub_err_t
+grub_iso9660_close (grub_file_t file)
+{
+  struct grub_iso9660_data *data =
+    (struct grub_iso9660_data *) file->data;
+  grub_free (data->node);
+  grub_free (data);
+
+  grub_dl_unref (my_mod);
+
+  return GRUB_ERR_NONE;
+}
+
+
+static grub_err_t
+grub_iso9660_label (grub_device_t device, char **label)
+{
+  struct grub_iso9660_data *data;
+  data = grub_iso9660_mount (device->disk);
+
+  if (data)
+    {
+      if (data->joliet)
+        *label = grub_iso9660_convert_string (data->voldesc.volname, 16);
+      else
+        *label = grub_strndup ((char *) data->voldesc.volname, 32);
+      if (*label)
+       {
+         char *ptr;
+         for (ptr = *label; *ptr;ptr++);
+         ptr--;
+         while (ptr >= *label && *ptr == ' ')
+           *ptr-- = 0;
+       }
+
+      grub_free (data);
+    }
+  else
+    *label = 0;
+
+  return grub_errno;
+}
+
+
+static grub_err_t
+grub_iso9660_uuid (grub_device_t device, char **uuid)
+{
+  struct grub_iso9660_data *data;
+  grub_disk_t disk = device->disk;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_iso9660_mount (disk);
+  if (data)
+    {
+      if (! data->voldesc.modified.year[0] && ! data->voldesc.modified.year[1]
+         && ! data->voldesc.modified.year[2] && ! data->voldesc.modified.year[3]
+         && ! data->voldesc.modified.month[0] && ! data->voldesc.modified.month[1]
+         && ! data->voldesc.modified.day[0] && ! data->voldesc.modified.day[1]
+         && ! data->voldesc.modified.hour[0] && ! data->voldesc.modified.hour[1]
+         && ! data->voldesc.modified.minute[0] && ! data->voldesc.modified.minute[1]
+         && ! data->voldesc.modified.second[0] && ! data->voldesc.modified.second[1]
+         && ! data->voldesc.modified.hundredth[0] && ! data->voldesc.modified.hundredth[1])
+       {
+         grub_error (GRUB_ERR_BAD_NUMBER, "no creation date in filesystem to generate UUID");
+         *uuid = NULL;
+       }
+      else
+       {
+         *uuid = grub_xasprintf ("%c%c%c%c-%c%c-%c%c-%c%c-%c%c-%c%c-%c%c",
+                                data->voldesc.modified.year[0],
+                                data->voldesc.modified.year[1],
+                                data->voldesc.modified.year[2],
+                                data->voldesc.modified.year[3],
+                                data->voldesc.modified.month[0],
+                                data->voldesc.modified.month[1],
+                                data->voldesc.modified.day[0],
+                                data->voldesc.modified.day[1],
+                                data->voldesc.modified.hour[0],
+                                data->voldesc.modified.hour[1],
+                                data->voldesc.modified.minute[0],
+                                data->voldesc.modified.minute[1],
+                                data->voldesc.modified.second[0],
+                                data->voldesc.modified.second[1],
+                                data->voldesc.modified.hundredth[0],
+                                data->voldesc.modified.hundredth[1]);
+       }
+    }
+  else
+    *uuid = NULL;
+
+       grub_dl_unref (my_mod);
+
+  grub_free (data);
+
+  return grub_errno;
+}
+
+/* Get writing time of filesystem. */
+static grub_err_t 
+grub_iso9660_mtime (grub_device_t device, grub_int32_t *timebuf)
+{
+  struct grub_iso9660_data *data;
+  grub_disk_t disk = device->disk;
+  grub_err_t err;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_iso9660_mount (disk);
+  if (!data)
+    {
+      grub_dl_unref (my_mod);
+      return grub_errno;
+    }
+  err = iso9660_to_unixtime (&data->voldesc.modified, timebuf);
+
+  grub_dl_unref (my_mod);
+
+  grub_free (data);
+
+  return err;
+}
+
+grub_uint64_t grub_iso9660_get_last_read_pos(grub_file_t file)
+{
+    (void)file;
+    return (g_ventoy_last_read_pos << GRUB_DISK_SECTOR_BITS);
+}
+
+grub_uint64_t grub_iso9660_get_last_file_dirent_pos(grub_file_t file)
+{
+    (void)file;
+    return (g_ventoy_last_file_dirent_pos << GRUB_DISK_SECTOR_BITS) + g_ventoy_last_file_dirent_offset;
+}
+
+static struct grub_fs grub_iso9660_fs =
+  {
+    .name = "iso9660",
+    .fs_dir = grub_iso9660_dir,
+    .fs_open = grub_iso9660_open,
+    .fs_read = grub_iso9660_read,
+    .fs_close = grub_iso9660_close,
+    .fs_label = grub_iso9660_label,
+    .fs_uuid = grub_iso9660_uuid,
+    .fs_mtime = grub_iso9660_mtime,
+#ifdef GRUB_UTIL
+    .reserved_first_sector = 1,
+    .blocklist_install = 1,
+#endif
+    .next = 0
+  };
+
+GRUB_MOD_INIT(iso9660)
+{
+  grub_fs_register (&grub_iso9660_fs);
+  my_mod = mod;
+}
+
+GRUB_MOD_FINI(iso9660)
+{
+  grub_fs_unregister (&grub_iso9660_fs);
+}
diff --git a/GRUB2/grub-2.04/grub-core/fs/udf.c b/GRUB2/grub-2.04/grub-core/fs/udf.c
new file mode 100644 (file)
index 0000000..c5ea54e
--- /dev/null
@@ -0,0 +1,1437 @@
+/* udf.c - Universal Disk Format filesystem.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2008,2009  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/err.h>
+#include <grub/file.h>
+#include <grub/mm.h>
+#include <grub/misc.h>
+#include <grub/disk.h>
+#include <grub/dl.h>
+#include <grub/types.h>
+#include <grub/fshelp.h>
+#include <grub/charset.h>
+#include <grub/datetime.h>
+#include <grub/udf.h>
+#include <grub/ventoy.h>
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+#define OFFSET_OF(TYPE, MEMBER) ((grub_size_t) &((TYPE *)0)->MEMBER)
+
+grub_uint32_t g_last_disk_read_sector = 0;
+grub_uint32_t g_last_fe_tag_ident = 0;
+grub_uint32_t g_last_icb_read_sector = 0;
+grub_uint32_t g_last_icb_read_sector_tag_ident = 0;
+grub_uint32_t g_last_fileattr_read_sector = 0;
+grub_uint32_t g_last_fileattr_read_sector_tag_ident = 0;
+grub_uint32_t g_last_fileattr_offset = 0;
+grub_uint64_t g_last_pd_length_offset = 0;
+
+#define GRUB_UDF_MAX_PDS               2
+#define GRUB_UDF_MAX_PMS               6
+
+#define U16                            grub_le_to_cpu16
+#define U32                            grub_le_to_cpu32
+#define U64                            grub_le_to_cpu64
+
+#define GRUB_UDF_TAG_IDENT_PVD         0x0001
+#define GRUB_UDF_TAG_IDENT_AVDP                0x0002
+#define GRUB_UDF_TAG_IDENT_VDP         0x0003
+#define GRUB_UDF_TAG_IDENT_IUVD                0x0004
+#define GRUB_UDF_TAG_IDENT_PD          0x0005
+#define GRUB_UDF_TAG_IDENT_LVD         0x0006
+#define GRUB_UDF_TAG_IDENT_USD         0x0007
+#define GRUB_UDF_TAG_IDENT_TD          0x0008
+#define GRUB_UDF_TAG_IDENT_LVID                0x0009
+
+#define GRUB_UDF_TAG_IDENT_FSD         0x0100
+#define GRUB_UDF_TAG_IDENT_FID         0x0101
+#define GRUB_UDF_TAG_IDENT_AED         0x0102
+#define GRUB_UDF_TAG_IDENT_IE          0x0103
+#define GRUB_UDF_TAG_IDENT_TE          0x0104
+#define GRUB_UDF_TAG_IDENT_FE          0x0105
+#define GRUB_UDF_TAG_IDENT_EAHD                0x0106
+#define GRUB_UDF_TAG_IDENT_USE         0x0107
+#define GRUB_UDF_TAG_IDENT_SBD         0x0108
+#define GRUB_UDF_TAG_IDENT_PIE         0x0109
+#define GRUB_UDF_TAG_IDENT_EFE         0x010A
+
+#define GRUB_UDF_ICBTAG_TYPE_UNDEF     0x00
+#define GRUB_UDF_ICBTAG_TYPE_USE       0x01
+#define GRUB_UDF_ICBTAG_TYPE_PIE       0x02
+#define GRUB_UDF_ICBTAG_TYPE_IE                0x03
+#define GRUB_UDF_ICBTAG_TYPE_DIRECTORY 0x04
+#define GRUB_UDF_ICBTAG_TYPE_REGULAR   0x05
+#define GRUB_UDF_ICBTAG_TYPE_BLOCK     0x06
+#define GRUB_UDF_ICBTAG_TYPE_CHAR      0x07
+#define GRUB_UDF_ICBTAG_TYPE_EA                0x08
+#define GRUB_UDF_ICBTAG_TYPE_FIFO      0x09
+#define GRUB_UDF_ICBTAG_TYPE_SOCKET    0x0A
+#define GRUB_UDF_ICBTAG_TYPE_TE                0x0B
+#define GRUB_UDF_ICBTAG_TYPE_SYMLINK   0x0C
+#define GRUB_UDF_ICBTAG_TYPE_STREAMDIR 0x0D
+
+#define GRUB_UDF_ICBTAG_FLAG_AD_MASK   0x0007
+#define GRUB_UDF_ICBTAG_FLAG_AD_SHORT  0x0000
+#define GRUB_UDF_ICBTAG_FLAG_AD_LONG   0x0001
+#define GRUB_UDF_ICBTAG_FLAG_AD_EXT    0x0002
+#define GRUB_UDF_ICBTAG_FLAG_AD_IN_ICB 0x0003
+
+#define GRUB_UDF_EXT_NORMAL            0x00000000
+#define GRUB_UDF_EXT_NREC_ALLOC                0x40000000
+#define GRUB_UDF_EXT_NREC_NALLOC       0x80000000
+#define GRUB_UDF_EXT_MASK              0xC0000000
+
+#define GRUB_UDF_FID_CHAR_HIDDEN       0x01
+#define GRUB_UDF_FID_CHAR_DIRECTORY    0x02
+#define GRUB_UDF_FID_CHAR_DELETED      0x04
+#define GRUB_UDF_FID_CHAR_PARENT       0x08
+#define GRUB_UDF_FID_CHAR_METADATA     0x10
+
+#define GRUB_UDF_STD_IDENT_BEA01       "BEA01"
+#define GRUB_UDF_STD_IDENT_BOOT2       "BOOT2"
+#define GRUB_UDF_STD_IDENT_CD001       "CD001"
+#define GRUB_UDF_STD_IDENT_CDW02       "CDW02"
+#define GRUB_UDF_STD_IDENT_NSR02       "NSR02"
+#define GRUB_UDF_STD_IDENT_NSR03       "NSR03"
+#define GRUB_UDF_STD_IDENT_TEA01       "TEA01"
+
+#define GRUB_UDF_CHARSPEC_TYPE_CS0     0x00
+#define GRUB_UDF_CHARSPEC_TYPE_CS1     0x01
+#define GRUB_UDF_CHARSPEC_TYPE_CS2     0x02
+#define GRUB_UDF_CHARSPEC_TYPE_CS3     0x03
+#define GRUB_UDF_CHARSPEC_TYPE_CS4     0x04
+#define GRUB_UDF_CHARSPEC_TYPE_CS5     0x05
+#define GRUB_UDF_CHARSPEC_TYPE_CS6     0x06
+#define GRUB_UDF_CHARSPEC_TYPE_CS7     0x07
+#define GRUB_UDF_CHARSPEC_TYPE_CS8     0x08
+
+#define GRUB_UDF_PARTMAP_TYPE_1                1
+#define GRUB_UDF_PARTMAP_TYPE_2                2
+
+struct grub_udf_lb_addr
+{
+  grub_uint32_t block_num;
+  grub_uint16_t part_ref;
+} GRUB_PACKED;
+
+struct grub_udf_short_ad
+{
+  grub_uint32_t length;
+  grub_uint32_t position;
+} GRUB_PACKED;
+
+struct grub_udf_long_ad
+{
+  grub_uint32_t length;
+  struct grub_udf_lb_addr block;
+  grub_uint8_t imp_use[6];
+} GRUB_PACKED;
+
+struct grub_udf_extent_ad
+{
+  grub_uint32_t length;
+  grub_uint32_t start;
+} GRUB_PACKED;
+
+struct grub_udf_charspec
+{
+  grub_uint8_t charset_type;
+  grub_uint8_t charset_info[63];
+} GRUB_PACKED;
+
+struct grub_udf_timestamp
+{
+  grub_uint16_t type_and_timezone;
+  grub_uint16_t year;
+  grub_uint8_t month;
+  grub_uint8_t day;
+  grub_uint8_t hour;
+  grub_uint8_t minute;
+  grub_uint8_t second;
+  grub_uint8_t centi_seconds;
+  grub_uint8_t hundreds_of_micro_seconds;
+  grub_uint8_t micro_seconds;
+} GRUB_PACKED;
+
+struct grub_udf_regid
+{
+  grub_uint8_t flags;
+  grub_uint8_t ident[23];
+  grub_uint8_t ident_suffix[8];
+} GRUB_PACKED;
+
+struct grub_udf_tag
+{
+  grub_uint16_t tag_ident;
+  grub_uint16_t desc_version;
+  grub_uint8_t tag_checksum;
+  grub_uint8_t reserved;
+  grub_uint16_t tag_serial_number;
+  grub_uint16_t desc_crc;
+  grub_uint16_t desc_crc_length;
+  grub_uint32_t tag_location;
+} GRUB_PACKED;
+
+struct grub_udf_fileset
+{
+  struct grub_udf_tag tag;
+  struct grub_udf_timestamp datetime;
+  grub_uint16_t interchange_level;
+  grub_uint16_t max_interchange_level;
+  grub_uint32_t charset_list;
+  grub_uint32_t max_charset_list;
+  grub_uint32_t fileset_num;
+  grub_uint32_t fileset_desc_num;
+  struct grub_udf_charspec vol_charset;
+  grub_uint8_t vol_ident[128];
+  struct grub_udf_charspec fileset_charset;
+  grub_uint8_t fileset_ident[32];
+  grub_uint8_t copyright_file_ident[32];
+  grub_uint8_t abstract_file_ident[32];
+  struct grub_udf_long_ad root_icb;
+  struct grub_udf_regid domain_ident;
+  struct grub_udf_long_ad next_ext;
+  struct grub_udf_long_ad streamdir_icb;
+} GRUB_PACKED;
+
+struct grub_udf_icbtag
+{
+  grub_uint32_t prior_recorded_num_direct_entries;
+  grub_uint16_t strategy_type;
+  grub_uint16_t strategy_parameter;
+  grub_uint16_t num_entries;
+  grub_uint8_t reserved;
+  grub_uint8_t file_type;
+  struct grub_udf_lb_addr parent_idb;
+  grub_uint16_t flags;
+} GRUB_PACKED;
+
+struct grub_udf_file_ident
+{
+  struct grub_udf_tag tag;
+  grub_uint16_t version_num;
+  grub_uint8_t characteristics;
+#define MAX_FILE_IDENT_LENGTH 256
+  grub_uint8_t file_ident_length;
+  struct grub_udf_long_ad icb;
+  grub_uint16_t imp_use_length;
+} GRUB_PACKED;
+
+struct grub_udf_file_entry
+{
+  struct grub_udf_tag tag;
+  struct grub_udf_icbtag icbtag;
+  grub_uint32_t uid;
+  grub_uint32_t gid;
+  grub_uint32_t permissions;
+  grub_uint16_t link_count;
+  grub_uint8_t record_format;
+  grub_uint8_t record_display_attr;
+  grub_uint32_t record_length;
+  grub_uint64_t file_size;
+  grub_uint64_t blocks_recorded;
+  struct grub_udf_timestamp access_time;
+  struct grub_udf_timestamp modification_time;
+  struct grub_udf_timestamp attr_time;
+  grub_uint32_t checkpoint;
+  struct grub_udf_long_ad extended_attr_idb;
+  struct grub_udf_regid imp_ident;
+  grub_uint64_t unique_id;
+  grub_uint32_t ext_attr_length;
+  grub_uint32_t alloc_descs_length;
+  grub_uint8_t ext_attr[0];
+} GRUB_PACKED;
+
+struct grub_udf_extended_file_entry
+{
+  struct grub_udf_tag tag;
+  struct grub_udf_icbtag icbtag;
+  grub_uint32_t uid;
+  grub_uint32_t gid;
+  grub_uint32_t permissions;
+  grub_uint16_t link_count;
+  grub_uint8_t record_format;
+  grub_uint8_t record_display_attr;
+  grub_uint32_t record_length;
+  grub_uint64_t file_size;
+  grub_uint64_t object_size;
+  grub_uint64_t blocks_recorded;
+  struct grub_udf_timestamp access_time;
+  struct grub_udf_timestamp modification_time;
+  struct grub_udf_timestamp create_time;
+  struct grub_udf_timestamp attr_time;
+  grub_uint32_t checkpoint;
+  grub_uint32_t reserved;
+  struct grub_udf_long_ad extended_attr_icb;
+  struct grub_udf_long_ad streamdir_icb;
+  struct grub_udf_regid imp_ident;
+  grub_uint64_t unique_id;
+  grub_uint32_t ext_attr_length;
+  grub_uint32_t alloc_descs_length;
+  grub_uint8_t ext_attr[0];
+} GRUB_PACKED;
+
+struct grub_udf_vrs
+{
+  grub_uint8_t type;
+  grub_uint8_t magic[5];
+  grub_uint8_t version;
+} GRUB_PACKED;
+
+struct grub_udf_avdp
+{
+  struct grub_udf_tag tag;
+  struct grub_udf_extent_ad vds;
+} GRUB_PACKED;
+
+struct grub_udf_pd
+{
+  struct grub_udf_tag tag;
+  grub_uint32_t seq_num;
+  grub_uint16_t flags;
+  grub_uint16_t part_num;
+  struct grub_udf_regid contents;
+  grub_uint8_t contents_use[128];
+  grub_uint32_t access_type;
+  grub_uint32_t start;
+  grub_uint32_t length;
+} GRUB_PACKED;
+
+struct grub_udf_partmap
+{
+  grub_uint8_t type;
+  grub_uint8_t length;
+  union
+  {
+    struct
+    {
+      grub_uint16_t seq_num;
+      grub_uint16_t part_num;
+    } type1;
+
+    struct
+    {
+      grub_uint8_t ident[62];
+    } type2;
+  };
+} GRUB_PACKED;
+
+struct grub_udf_pvd
+{
+  struct grub_udf_tag tag;
+  grub_uint32_t seq_num;
+  grub_uint32_t pvd_num;
+  grub_uint8_t ident[32];
+  grub_uint16_t vol_seq_num;
+  grub_uint16_t max_vol_seq_num;
+  grub_uint16_t interchange_level;
+  grub_uint16_t max_interchange_level;
+  grub_uint32_t charset_list;
+  grub_uint32_t max_charset_list;
+  grub_uint8_t volset_ident[128];
+  struct grub_udf_charspec desc_charset;
+  struct grub_udf_charspec expl_charset;
+  struct grub_udf_extent_ad vol_abstract;
+  struct grub_udf_extent_ad vol_copyright;
+  struct grub_udf_regid app_ident;
+  struct grub_udf_timestamp recording_time;
+  struct grub_udf_regid imp_ident;
+  grub_uint8_t imp_use[64];
+  grub_uint32_t pred_vds_loc;
+  grub_uint16_t flags;
+  grub_uint8_t reserved[22];
+} GRUB_PACKED;
+
+struct grub_udf_lvd
+{
+  struct grub_udf_tag tag;
+  grub_uint32_t seq_num;
+  struct grub_udf_charspec charset;
+  grub_uint8_t ident[128];
+  grub_uint32_t bsize;
+  struct grub_udf_regid domain_ident;
+  struct grub_udf_long_ad root_fileset;
+  grub_uint32_t map_table_length;
+  grub_uint32_t num_part_maps;
+  struct grub_udf_regid imp_ident;
+  grub_uint8_t imp_use[128];
+  struct grub_udf_extent_ad integrity_seq_ext;
+  grub_uint8_t part_maps[1608];
+} GRUB_PACKED;
+
+struct grub_udf_aed
+{
+  struct grub_udf_tag tag;
+  grub_uint32_t prev_ae;
+  grub_uint32_t ae_len;
+} GRUB_PACKED;
+
+struct grub_udf_data
+{
+  grub_disk_t disk;
+  struct grub_udf_pvd pvd;
+  struct grub_udf_lvd lvd;
+  struct grub_udf_pd pds[GRUB_UDF_MAX_PDS];
+  struct grub_udf_partmap *pms[GRUB_UDF_MAX_PMS];
+  struct grub_udf_long_ad root_icb;
+  int npd, npm, lbshift;
+};
+
+struct grub_fshelp_node
+{
+  struct grub_udf_data *data;
+  int part_ref;
+  union
+  {
+    struct grub_udf_file_entry fe;
+    struct grub_udf_extended_file_entry efe;
+    char raw[0];
+  } block;
+};
+
+static inline grub_size_t
+get_fshelp_size (struct grub_udf_data *data)
+{
+  struct grub_fshelp_node *x = NULL;
+  return sizeof (*x)
+    + (1 << (GRUB_DISK_SECTOR_BITS
+            + data->lbshift)) - sizeof (x->block);
+}
+
+static grub_dl_t my_mod;
+
+static grub_uint32_t
+grub_udf_get_block (struct grub_udf_data *data,
+                   grub_uint16_t part_ref, grub_uint32_t block)
+{
+  part_ref = U16 (part_ref);
+
+  if (part_ref >= data->npm)
+    {
+      grub_error (GRUB_ERR_BAD_FS, "invalid part ref");
+      return 0;
+    }
+
+  return (U32 (data->pds[data->pms[part_ref]->type1.part_num].start)
+          + U32 (block));
+}
+
+static grub_err_t
+grub_udf_read_icb (struct grub_udf_data *data,
+                  struct grub_udf_long_ad *icb,
+                  struct grub_fshelp_node *node)
+{
+  grub_uint32_t block;
+
+  block = grub_udf_get_block (data,
+                             icb->block.part_ref,
+                              icb->block.block_num);
+
+  if (grub_errno)
+    return grub_errno;
+
+  if (grub_disk_read (data->disk, block << data->lbshift, 0,
+                     1 << (GRUB_DISK_SECTOR_BITS
+                           + data->lbshift),
+                     &node->block))
+    return grub_errno;
+
+  g_last_disk_read_sector = block;
+  g_last_fe_tag_ident = U16(node->block.fe.tag.tag_ident);
+
+  if ((U16 (node->block.fe.tag.tag_ident) != GRUB_UDF_TAG_IDENT_FE) &&
+      (U16 (node->block.fe.tag.tag_ident) != GRUB_UDF_TAG_IDENT_EFE))
+    return grub_error (GRUB_ERR_BAD_FS, "invalid fe/efe descriptor");
+
+  node->part_ref = icb->block.part_ref;
+  node->data = data;
+  return 0;
+}
+
+static grub_disk_addr_t
+grub_udf_read_block (grub_fshelp_node_t node, grub_disk_addr_t fileblock)
+{
+  char *buf = NULL;
+  char *ptr;
+  grub_ssize_t len;
+  grub_disk_addr_t filebytes;
+
+  switch (U16 (node->block.fe.tag.tag_ident))
+    {
+    case GRUB_UDF_TAG_IDENT_FE:
+      ptr = (char *) &node->block.fe.ext_attr[0] + U32 (node->block.fe.ext_attr_length);
+      len = U32 (node->block.fe.alloc_descs_length);
+      break;
+
+    case GRUB_UDF_TAG_IDENT_EFE:
+      ptr = (char *) &node->block.efe.ext_attr[0] + U32 (node->block.efe.ext_attr_length);
+      len = U32 (node->block.efe.alloc_descs_length);
+      break;
+
+    default:
+      grub_error (GRUB_ERR_BAD_FS, "invalid file entry");
+      return 0;
+    }
+
+  if ((U16 (node->block.fe.icbtag.flags) & GRUB_UDF_ICBTAG_FLAG_AD_MASK)
+      == GRUB_UDF_ICBTAG_FLAG_AD_SHORT)
+    {
+      struct grub_udf_short_ad *ad = (struct grub_udf_short_ad *) ptr;
+
+      filebytes = fileblock * U32 (node->data->lvd.bsize);
+      while (len >= (grub_ssize_t) sizeof (struct grub_udf_short_ad))
+       {
+         grub_uint32_t adlen = U32 (ad->length) & 0x3fffffff;
+         grub_uint32_t adtype = U32 (ad->length) >> 30;
+         if (adtype == 3)
+           {
+             struct grub_udf_aed *extension;
+             grub_disk_addr_t sec = grub_udf_get_block(node->data,
+                                                       node->part_ref,
+                                                       ad->position);
+             if (!buf)
+               {
+                 buf = grub_malloc (U32 (node->data->lvd.bsize));
+                 if (!buf)
+                   return 0;
+               }
+             if (grub_disk_read (node->data->disk, sec << node->data->lbshift,
+                                 0, adlen, buf))
+               goto fail;
+
+             extension = (struct grub_udf_aed *) buf;
+             if (U16 (extension->tag.tag_ident) != GRUB_UDF_TAG_IDENT_AED)
+               {
+                 grub_error (GRUB_ERR_BAD_FS, "invalid aed tag");
+                 goto fail;
+               }
+
+             len = U32 (extension->ae_len);
+             ad = (struct grub_udf_short_ad *)
+                   (buf + sizeof (struct grub_udf_aed));
+             continue;
+           }
+
+         if (filebytes < adlen)
+           {
+             grub_uint32_t ad_pos = ad->position;
+             grub_free (buf);
+             return ((U32 (ad_pos) & GRUB_UDF_EXT_MASK) ? 0 :
+                     (grub_udf_get_block (node->data, node->part_ref, ad_pos)
+                      + (filebytes >> (GRUB_DISK_SECTOR_BITS
+                                       + node->data->lbshift))));
+           }
+
+         filebytes -= adlen;
+         ad++;
+         len -= sizeof (struct grub_udf_short_ad);
+       }
+    }
+  else
+    {
+      struct grub_udf_long_ad *ad = (struct grub_udf_long_ad *) ptr;
+
+      filebytes = fileblock * U32 (node->data->lvd.bsize);
+      while (len >= (grub_ssize_t) sizeof (struct grub_udf_long_ad))
+       {
+         grub_uint32_t adlen = U32 (ad->length) & 0x3fffffff;
+         grub_uint32_t adtype = U32 (ad->length) >> 30;
+         if (adtype == 3)
+           {
+             struct grub_udf_aed *extension;
+             grub_disk_addr_t sec = grub_udf_get_block(node->data,
+                                                       ad->block.part_ref,
+                                                       ad->block.block_num);
+             if (!buf)
+               {
+                 buf = grub_malloc (U32 (node->data->lvd.bsize));
+                 if (!buf)
+                   return 0;
+               }
+             if (grub_disk_read (node->data->disk, sec << node->data->lbshift,
+                                 0, adlen, buf))
+               goto fail;
+
+             extension = (struct grub_udf_aed *) buf;
+             if (U16 (extension->tag.tag_ident) != GRUB_UDF_TAG_IDENT_AED)
+               {
+                 grub_error (GRUB_ERR_BAD_FS, "invalid aed tag");
+                 goto fail;
+               }
+
+             len = U32 (extension->ae_len);
+             ad = (struct grub_udf_long_ad *)
+                   (buf + sizeof (struct grub_udf_aed));
+             continue;
+           }
+             
+         if (filebytes < adlen)
+           {
+             grub_uint32_t ad_block_num = ad->block.block_num;
+             grub_uint32_t ad_part_ref = ad->block.part_ref;
+             grub_free (buf);
+             return ((U32 (ad_block_num) & GRUB_UDF_EXT_MASK) ?  0 :
+                     (grub_udf_get_block (node->data, ad_part_ref,
+                                          ad_block_num)
+                      + (filebytes >> (GRUB_DISK_SECTOR_BITS
+                                       + node->data->lbshift))));
+           }
+
+         filebytes -= adlen;
+         ad++;
+         len -= sizeof (struct grub_udf_long_ad);
+       }
+    }
+
+fail:
+  grub_free (buf);
+
+  return 0;
+}
+
+static grub_ssize_t
+grub_udf_read_file (grub_fshelp_node_t node,
+                   grub_disk_read_hook_t read_hook, void *read_hook_data,
+                   grub_off_t pos, grub_size_t len, char *buf)
+{
+  switch (U16 (node->block.fe.icbtag.flags) & GRUB_UDF_ICBTAG_FLAG_AD_MASK)
+    {
+    case GRUB_UDF_ICBTAG_FLAG_AD_IN_ICB:
+      {
+       char *ptr;
+
+       ptr = ((U16 (node->block.fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_FE) ?
+              ((char *) &node->block.fe.ext_attr[0]
+                + U32 (node->block.fe.ext_attr_length)) :
+              ((char *) &node->block.efe.ext_attr[0]
+                + U32 (node->block.efe.ext_attr_length)));
+
+       grub_memcpy (buf, ptr + pos, len);
+
+       return len;
+      }
+
+    case GRUB_UDF_ICBTAG_FLAG_AD_EXT:
+      grub_error (GRUB_ERR_BAD_FS, "invalid extent type");
+      return 0;
+    }
+
+  return grub_fshelp_read_file (node->data->disk, node,
+                               read_hook, read_hook_data,
+                               pos, len, buf, grub_udf_read_block,
+                               U64 (node->block.fe.file_size),
+                               node->data->lbshift, 0);
+}
+
+static unsigned sblocklist[] = { 256, 512, 0 };
+
+static struct grub_udf_data *
+grub_udf_mount (grub_disk_t disk)
+{
+  struct grub_udf_data *data = 0;
+  struct grub_udf_fileset root_fs;
+  unsigned *sblklist;
+  grub_uint32_t block, vblock;
+  int i, lbshift;
+
+  data = grub_malloc (sizeof (struct grub_udf_data));
+  if (!data)
+    return 0;
+
+  data->disk = disk;
+
+  /* Search for Anchor Volume Descriptor Pointer (AVDP)
+   * and determine logical block size.  */
+  block = 0;
+  for (lbshift = 0; lbshift < 4; lbshift++)
+    {
+      for (sblklist = sblocklist; *sblklist; sblklist++)
+        {
+         struct grub_udf_avdp avdp;
+
+         if (grub_disk_read (disk, *sblklist << lbshift, 0,
+                             sizeof (struct grub_udf_avdp), &avdp))
+           {
+             grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
+             goto fail;
+           }
+
+         if (U16 (avdp.tag.tag_ident) == GRUB_UDF_TAG_IDENT_AVDP &&
+             U32 (avdp.tag.tag_location) == *sblklist)
+           {
+             block = U32 (avdp.vds.start);
+             break;
+           }
+       }
+
+      if (block)
+       break;
+    }
+
+  if (!block)
+    {
+      grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
+      goto fail;
+    }
+  data->lbshift = lbshift;
+
+  /* Search for Volume Recognition Sequence (VRS).  */
+  for (vblock = (32767 >> (lbshift + GRUB_DISK_SECTOR_BITS)) + 1;;
+       vblock += (2047 >> (lbshift + GRUB_DISK_SECTOR_BITS)) + 1)
+    {
+      struct grub_udf_vrs vrs;
+
+      if (grub_disk_read (disk, vblock << lbshift, 0,
+                         sizeof (struct grub_udf_vrs), &vrs))
+       {
+         grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
+         goto fail;
+       }
+
+      if ((!grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_NSR03, 5)) ||
+         (!grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_NSR02, 5)))
+       break;
+
+      if ((grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_BEA01, 5)) &&
+         (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_BOOT2, 5)) &&
+         (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_CD001, 5)) &&
+         (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_CDW02, 5)) &&
+         (grub_memcmp (vrs.magic, GRUB_UDF_STD_IDENT_TEA01, 5)))
+       {
+         grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
+         goto fail;
+       }
+    }
+
+  data->npd = data->npm = 0;
+  /* Locate Partition Descriptor (PD) and Logical Volume Descriptor (LVD).  */
+  while (1)
+    {
+      struct grub_udf_tag tag;
+
+      if (grub_disk_read (disk, block << lbshift, 0,
+                         sizeof (struct grub_udf_tag), &tag))
+       {
+         grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
+         goto fail;
+       }
+
+      tag.tag_ident = U16 (tag.tag_ident);
+      if (tag.tag_ident == GRUB_UDF_TAG_IDENT_PVD)
+       {
+         if (grub_disk_read (disk, block << lbshift, 0,
+                             sizeof (struct grub_udf_pvd),
+                             &data->pvd))
+           {
+             grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
+             goto fail;
+           }
+       }
+      else if (tag.tag_ident == GRUB_UDF_TAG_IDENT_PD)
+       {
+         if (data->npd >= GRUB_UDF_MAX_PDS)
+           {
+             grub_error (GRUB_ERR_BAD_FS, "too many PDs");
+             goto fail;
+           }
+
+         if (grub_disk_read (disk, block << lbshift, 0,
+                             sizeof (struct grub_udf_pd),
+                             &data->pds[data->npd]))
+           {
+             grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
+             goto fail;
+           }
+
+      g_last_pd_length_offset = (block << lbshift) * 512 + OFFSET_OF(struct grub_udf_pd, length);
+
+         data->npd++;
+       }
+      else if (tag.tag_ident == GRUB_UDF_TAG_IDENT_LVD)
+       {
+         int k;
+
+         struct grub_udf_partmap *ppm;
+
+         if (grub_disk_read (disk, block << lbshift, 0,
+                             sizeof (struct grub_udf_lvd),
+                             &data->lvd))
+           {
+             grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
+             goto fail;
+           }
+
+         if (data->npm + U32 (data->lvd.num_part_maps) > GRUB_UDF_MAX_PMS)
+           {
+             grub_error (GRUB_ERR_BAD_FS, "too many partition maps");
+             goto fail;
+           }
+
+         ppm = (struct grub_udf_partmap *) &data->lvd.part_maps;
+         for (k = U32 (data->lvd.num_part_maps); k > 0; k--)
+           {
+             if (ppm->type != GRUB_UDF_PARTMAP_TYPE_1)
+               {
+                 grub_error (GRUB_ERR_BAD_FS, "partmap type not supported");
+                 goto fail;
+               }
+
+             data->pms[data->npm++] = ppm;
+             ppm = (struct grub_udf_partmap *) ((char *) ppm +
+                                                 U32 (ppm->length));
+           }
+       }
+      else if (tag.tag_ident > GRUB_UDF_TAG_IDENT_TD)
+       {
+         grub_error (GRUB_ERR_BAD_FS, "invalid tag ident");
+         goto fail;
+       }
+      else if (tag.tag_ident == GRUB_UDF_TAG_IDENT_TD)
+       break;
+
+      block++;
+    }
+
+  for (i = 0; i < data->npm; i++)
+    {
+      int j;
+
+      for (j = 0; j < data->npd; j++)
+       if (data->pms[i]->type1.part_num == data->pds[j].part_num)
+         {
+           data->pms[i]->type1.part_num = j;
+           break;
+         }
+
+      if (j == data->npd)
+       {
+         grub_error (GRUB_ERR_BAD_FS, "can\'t find PD");
+         goto fail;
+       }
+    }
+
+  block = grub_udf_get_block (data,
+                             data->lvd.root_fileset.block.part_ref,
+                             data->lvd.root_fileset.block.block_num);
+
+  if (grub_errno)
+    goto fail;
+
+  if (grub_disk_read (disk, block << lbshift, 0,
+                     sizeof (struct grub_udf_fileset), &root_fs))
+    {
+      grub_error (GRUB_ERR_BAD_FS, "not an UDF filesystem");
+      goto fail;
+    }
+
+  if (U16 (root_fs.tag.tag_ident) != GRUB_UDF_TAG_IDENT_FSD)
+    {
+      grub_error (GRUB_ERR_BAD_FS, "invalid fileset descriptor");
+      goto fail;
+    }
+
+  data->root_icb = root_fs.root_icb;
+
+  return data;
+
+fail:
+  grub_free (data);
+  return 0;
+}
+
+#ifdef GRUB_UTIL
+grub_disk_addr_t
+grub_udf_get_cluster_sector (grub_disk_t disk, grub_uint64_t *sec_per_lcn)
+{
+  grub_disk_addr_t ret;
+  static struct grub_udf_data *data;
+
+  data = grub_udf_mount (disk);
+  if (!data)
+    return 0;
+
+  ret = U32 (data->pds[data->pms[0]->type1.part_num].start);
+  *sec_per_lcn = 1ULL << data->lbshift;
+  grub_free (data);
+  return ret;
+}
+#endif
+
+static char *
+read_string (const grub_uint8_t *raw, grub_size_t sz, char *outbuf)
+{
+  grub_uint16_t *utf16 = NULL;
+  grub_size_t utf16len = 0;
+
+  if (sz == 0)
+    return NULL;
+
+  if (raw[0] != 8 && raw[0] != 16)
+    return NULL;
+
+  if (raw[0] == 8)
+    {
+      unsigned i;
+      utf16len = sz - 1;
+      utf16 = grub_malloc (utf16len * sizeof (utf16[0]));
+      if (!utf16)
+       return NULL;
+      for (i = 0; i < utf16len; i++)
+       utf16[i] = raw[i + 1];
+    }
+  if (raw[0] == 16)
+    {
+      unsigned i;
+      utf16len = (sz - 1) / 2;
+      utf16 = grub_malloc (utf16len * sizeof (utf16[0]));
+      if (!utf16)
+       return NULL;
+      for (i = 0; i < utf16len; i++)
+       utf16[i] = (raw[2 * i + 1] << 8) | raw[2*i + 2];
+    }
+  if (!outbuf)
+    outbuf = grub_malloc (utf16len * GRUB_MAX_UTF8_PER_UTF16 + 1);
+  if (outbuf)
+    *grub_utf16_to_utf8 ((grub_uint8_t *) outbuf, utf16, utf16len) = '\0';
+  grub_free (utf16);
+  return outbuf;
+}
+
+static char *
+read_dstring (const grub_uint8_t *raw, grub_size_t sz)
+{
+  grub_size_t len;
+
+  if (raw[0] == 0) {
+      char *outbuf = grub_malloc (1);
+      if (!outbuf)
+       return NULL;
+      outbuf[0] = 0;
+      return outbuf;
+    }
+
+  len = raw[sz - 1];
+  if (len > sz - 1)
+    len = sz - 1;
+  return read_string (raw, len, NULL);
+}
+
+static int
+grub_udf_iterate_dir (grub_fshelp_node_t dir,
+                     grub_fshelp_iterate_dir_hook_t hook, void *hook_data)
+{
+  grub_fshelp_node_t child;
+  struct grub_udf_file_ident dirent;
+  grub_off_t offset = 0;
+
+  child = grub_malloc (get_fshelp_size (dir->data));
+  if (!child)
+    return 0;
+
+  /* The current directory is not stored.  */
+  grub_memcpy (child, dir, get_fshelp_size (dir->data));
+
+  if (hook (".", GRUB_FSHELP_DIR, child, hook_data))
+    return 1;
+
+  while (offset < U64 (dir->block.fe.file_size))
+    {
+      if (grub_udf_read_file (dir, 0, 0, offset, sizeof (dirent),
+                             (char *) &dirent) != sizeof (dirent))
+       return 0;
+
+      if (U16 (dirent.tag.tag_ident) != GRUB_UDF_TAG_IDENT_FID)
+       {
+         grub_error (GRUB_ERR_BAD_FS, "invalid fid tag");
+         return 0;
+       }
+
+      offset += sizeof (dirent) + U16 (dirent.imp_use_length);
+      if (!(dirent.characteristics & GRUB_UDF_FID_CHAR_DELETED))
+       {
+         child = grub_malloc (get_fshelp_size (dir->data));
+         if (!child)
+           return 0;
+
+          if (grub_udf_read_icb (dir->data, &dirent.icb, child))
+           return 0;
+
+          g_last_icb_read_sector = g_last_disk_read_sector;
+          g_last_icb_read_sector_tag_ident = g_last_fe_tag_ident;
+          if (dirent.characteristics & GRUB_UDF_FID_CHAR_PARENT)
+           {
+             /* This is the parent directory.  */
+             if (hook ("..", GRUB_FSHELP_DIR, child, hook_data))
+               return 1;
+           }
+          else
+           {
+             enum grub_fshelp_filetype type;
+             char *filename;
+             grub_uint8_t raw[MAX_FILE_IDENT_LENGTH];
+
+             type = ((dirent.characteristics & GRUB_UDF_FID_CHAR_DIRECTORY) ?
+                     (GRUB_FSHELP_DIR) : (GRUB_FSHELP_REG));
+             if (child->block.fe.icbtag.file_type == GRUB_UDF_ICBTAG_TYPE_SYMLINK)
+               type = GRUB_FSHELP_SYMLINK;
+
+             if ((grub_udf_read_file (dir, 0, 0, offset,
+                                      dirent.file_ident_length,
+                                      (char *) raw))
+                 != dirent.file_ident_length)
+               return 0;
+
+             filename = read_string (raw, dirent.file_ident_length, 0);
+             if (!filename)
+               grub_print_error ();
+
+             if (filename && hook (filename, type, child, hook_data))
+               {
+                   g_last_fileattr_read_sector = g_last_icb_read_sector;
+            g_last_fileattr_read_sector_tag_ident = g_last_icb_read_sector_tag_ident;
+            g_last_fileattr_offset = (grub_uint32_t)((child->block.fe.ext_attr + child->block.fe.ext_attr_length) - (grub_uint8_t *)&(child->block.fe));
+                 grub_free (filename);
+                 return 1;
+               }
+             grub_free (filename);
+           }
+       }
+
+      /* Align to dword boundary.  */
+      offset = (offset + dirent.file_ident_length + 3) & (~3);
+    }
+
+  return 0;
+}
+
+static char *
+grub_udf_read_symlink (grub_fshelp_node_t node)
+{
+  grub_size_t sz = U64 (node->block.fe.file_size);
+  grub_uint8_t *raw;
+  const grub_uint8_t *ptr;
+  char *out, *optr;
+
+  if (sz < 4)
+    return NULL;
+  raw = grub_malloc (sz);
+  if (!raw)
+    return NULL;
+  if (grub_udf_read_file (node, NULL, NULL, 0, sz, (char *) raw) < 0)
+    {
+      grub_free (raw);
+      return NULL;
+    }
+
+  out = grub_malloc (sz * 2 + 1);
+  if (!out)
+    {
+      grub_free (raw);
+      return NULL;
+    }
+
+  optr = out;
+
+  for (ptr = raw; ptr < raw + sz; )
+    {
+      grub_size_t s;
+      if ((grub_size_t) (ptr - raw + 4) > sz)
+       goto fail;
+      if (!(ptr[2] == 0 && ptr[3] == 0))
+       goto fail;
+      s = 4 + ptr[1];
+      if ((grub_size_t) (ptr - raw + s) > sz)
+       goto fail;
+      switch (*ptr)
+       {
+       case 1:
+         if (ptr[1])
+           goto fail;
+         /* Fallthrough.  */
+       case 2:
+         /* in 4 bytes. out: 1 byte.  */
+         optr = out;
+         *optr++ = '/';
+         break;
+       case 3:
+         /* in 4 bytes. out: 3 bytes.  */
+         if (optr != out)
+           *optr++ = '/';
+         *optr++ = '.';
+         *optr++ = '.';
+         break;
+       case 4:
+         /* in 4 bytes. out: 2 bytes.  */
+         if (optr != out)
+           *optr++ = '/';
+         *optr++ = '.';
+         break;
+       case 5:
+         /* in 4 + n bytes. out, at most: 1 + 2 * n bytes.  */
+         if (optr != out)
+           *optr++ = '/';
+         if (!read_string (ptr + 4, s - 4, optr))
+           goto fail;
+         optr += grub_strlen (optr);
+         break;
+       default:
+         goto fail;
+       }
+      ptr += s;
+    }
+  *optr = 0;
+  grub_free (raw);
+  return out;
+
+ fail:
+  grub_free (raw);
+  grub_free (out);
+  grub_error (GRUB_ERR_BAD_FS, "invalid symlink");
+  return NULL;
+}
+
+/* Context for grub_udf_dir.  */
+struct grub_udf_dir_ctx
+{
+  grub_fs_dir_hook_t hook;
+  void *hook_data;
+};
+
+/* Helper for grub_udf_dir.  */
+static int
+grub_udf_dir_iter (const char *filename, enum grub_fshelp_filetype filetype,
+                  grub_fshelp_node_t node, void *data)
+{
+  struct grub_udf_dir_ctx *ctx = data;
+  struct grub_dirhook_info info;
+  const struct grub_udf_timestamp *tstamp = NULL;
+
+  grub_memset (&info, 0, sizeof (info));
+  info.dir = ((filetype & GRUB_FSHELP_TYPE_MASK) == GRUB_FSHELP_DIR);
+  if (U16 (node->block.fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_FE)
+    tstamp = &node->block.fe.modification_time;
+  else if (U16 (node->block.fe.tag.tag_ident) == GRUB_UDF_TAG_IDENT_EFE)
+    tstamp = &node->block.efe.modification_time;
+
+  if (tstamp && (U16 (tstamp->type_and_timezone) & 0xf000) == 0x1000)
+    {
+      grub_int16_t tz;
+      struct grub_datetime datetime;
+
+      datetime.year = U16 (tstamp->year);
+      datetime.month = tstamp->month;
+      datetime.day = tstamp->day;
+      datetime.hour = tstamp->hour;
+      datetime.minute = tstamp->minute;
+      datetime.second = tstamp->second;
+
+      tz = U16 (tstamp->type_and_timezone) & 0xfff;
+      if (tz & 0x800)
+       tz |= 0xf000;
+      if (tz == -2047)
+       tz = 0;
+
+      info.mtimeset = !!grub_datetime2unixtime (&datetime, &info.mtime);
+
+      info.mtime -= 60 * tz;
+    }
+  grub_free (node);
+  return ctx->hook (filename, &info, ctx->hook_data);
+}
+
+static grub_err_t
+grub_udf_dir (grub_device_t device, const char *path,
+             grub_fs_dir_hook_t hook, void *hook_data)
+{
+  struct grub_udf_dir_ctx ctx = { hook, hook_data };
+  struct grub_udf_data *data = 0;
+  struct grub_fshelp_node *rootnode = 0;
+  struct grub_fshelp_node *foundnode = 0;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_udf_mount (device->disk);
+  if (!data)
+    goto fail;
+
+  rootnode = grub_malloc (get_fshelp_size (data));
+  if (!rootnode)
+    goto fail;
+
+  if (grub_udf_read_icb (data, &data->root_icb, rootnode))
+    goto fail;
+
+  if (grub_fshelp_find_file (path, rootnode,
+                            &foundnode,
+                            grub_udf_iterate_dir, grub_udf_read_symlink,
+                            GRUB_FSHELP_DIR))
+    goto fail;
+
+  grub_udf_iterate_dir (foundnode, grub_udf_dir_iter, &ctx);
+
+  if (foundnode != rootnode)
+    grub_free (foundnode);
+
+fail:
+  grub_free (rootnode);
+
+  grub_free (data);
+
+  grub_dl_unref (my_mod);
+
+  return grub_errno;
+}
+
+static grub_err_t
+grub_udf_open (struct grub_file *file, const char *name)
+{
+  struct grub_udf_data *data;
+  struct grub_fshelp_node *rootnode = 0;
+  struct grub_fshelp_node *foundnode;
+
+  grub_dl_ref (my_mod);
+
+  data = grub_udf_mount (file->device->disk);
+  if (!data)
+    goto fail;
+
+  rootnode = grub_malloc (get_fshelp_size (data));
+  if (!rootnode)
+    goto fail;
+
+  if (grub_udf_read_icb (data, &data->root_icb, rootnode))
+    goto fail;
+
+  if (grub_fshelp_find_file (name, rootnode,
+                            &foundnode,
+                            grub_udf_iterate_dir, grub_udf_read_symlink,
+                            GRUB_FSHELP_REG))
+    goto fail;
+
+  file->data = foundnode;
+  file->offset = 0;
+  file->size = U64 (foundnode->block.fe.file_size);
+
+  grub_free (rootnode);
+
+  return 0;
+
+fail:
+  grub_dl_unref (my_mod);
+
+  grub_free (data);
+  grub_free (rootnode);
+
+  return grub_errno;
+}
+
+static grub_ssize_t
+grub_udf_read (grub_file_t file, char *buf, grub_size_t len)
+{
+  struct grub_fshelp_node *node = (struct grub_fshelp_node *) file->data;
+
+  return grub_udf_read_file (node, file->read_hook, file->read_hook_data,
+                            file->offset, len, buf);
+}
+
+static grub_err_t
+grub_udf_close (grub_file_t file)
+{
+  if (file->data)
+    {
+      struct grub_fshelp_node *node = (struct grub_fshelp_node *) file->data;
+
+      grub_free (node->data);
+      grub_free (node);
+    }
+
+  grub_dl_unref (my_mod);
+
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_udf_label (grub_device_t device, char **label)
+{
+  struct grub_udf_data *data;
+  data = grub_udf_mount (device->disk);
+
+  if (data)
+    {
+      *label = read_dstring (data->lvd.ident, sizeof (data->lvd.ident));
+      grub_free (data);
+    }
+  else
+    *label = 0;
+
+  return grub_errno;
+}
+
+static char *
+gen_uuid_from_volset (char *volset_ident)
+{
+  grub_size_t i;
+  grub_size_t len;
+  grub_size_t nonhexpos;
+  grub_uint8_t buf[17];
+  char *uuid;
+
+  len = grub_strlen (volset_ident);
+  if (len < 8)
+    return NULL;
+
+  uuid = grub_malloc (17);
+  if (!uuid)
+    return NULL;
+
+  if (len > 16)
+    len = 16;
+
+  grub_memset (buf, 0, sizeof (buf));
+  grub_memcpy (buf, volset_ident, len);
+
+  nonhexpos = 16;
+  for (i = 0; i < 16; ++i)
+    {
+      if (!grub_isxdigit (buf[i]))
+        {
+          nonhexpos = i;
+          break;
+        }
+    }
+
+  if (nonhexpos < 8)
+    {
+      grub_snprintf (uuid, 17, "%02x%02x%02x%02x%02x%02x%02x%02x",
+                    buf[0], buf[1], buf[2], buf[3],
+                    buf[4], buf[5], buf[6], buf[7]);
+    }
+  else if (nonhexpos < 16)
+    {
+      for (i = 0; i < 8; ++i)
+        uuid[i] = grub_tolower (buf[i]);
+      grub_snprintf (uuid+8, 9, "%02x%02x%02x%02x",
+                    buf[8], buf[9], buf[10], buf[11]);
+    }
+  else
+    {
+      for (i = 0; i < 16; ++i)
+        uuid[i] = grub_tolower (buf[i]);
+      uuid[16] = 0;
+    }
+
+  return uuid;
+}
+
+static grub_err_t
+grub_udf_uuid (grub_device_t device, char **uuid)
+{
+  char *volset_ident;
+  struct grub_udf_data *data;
+  data = grub_udf_mount (device->disk);
+
+  if (data)
+    {
+      volset_ident = read_dstring (data->pvd.volset_ident, sizeof (data->pvd.volset_ident));
+      if (volset_ident)
+        {
+          *uuid = gen_uuid_from_volset (volset_ident);
+          grub_free (volset_ident);
+        }
+      else
+        *uuid = 0;
+      grub_free (data);
+    }
+  else
+    *uuid = 0;
+
+  return grub_errno;
+}
+
+grub_uint64_t grub_udf_get_file_offset(grub_file_t file)
+{
+    grub_disk_addr_t sector;
+    struct grub_fshelp_node *node = (struct grub_fshelp_node *)file->data;
+    
+    sector = grub_udf_read_block(node, 0);
+    
+    return 512 * (sector << node->data->lbshift);
+}
+
+grub_uint64_t grub_udf_get_last_pd_size_offset(void)
+{
+    return g_last_pd_length_offset;
+}
+
+grub_uint64_t grub_udf_get_last_file_attr_offset
+(
+    grub_file_t file, 
+    grub_uint32_t *startBlock,
+    grub_uint64_t *fe_entry_size_offset
+)
+{
+    grub_uint64_t attr_offset;
+    struct grub_fshelp_node *node;
+    struct grub_udf_data *data;
+
+    node = (struct grub_fshelp_node *)file->data;
+    data = node->data;
+
+    *startBlock = data->pds[data->pms[0]->type1.part_num].start;
+
+    attr_offset = g_last_fileattr_read_sector * 2048 + g_last_fileattr_offset;
+    
+    if (GRUB_UDF_TAG_IDENT_FE == g_last_fileattr_read_sector_tag_ident)
+    {
+        *fe_entry_size_offset = g_last_fileattr_read_sector * 2048 + OFFSET_OF(struct grub_udf_file_entry, file_size);
+    }
+    else
+    {
+        *fe_entry_size_offset = g_last_fileattr_read_sector * 2048 + OFFSET_OF(struct grub_udf_extended_file_entry, file_size);
+    }
+
+    return attr_offset;
+}
+
+static struct grub_fs grub_udf_fs = {
+  .name = "udf",
+  .fs_dir = grub_udf_dir,
+  .fs_open = grub_udf_open,
+  .fs_read = grub_udf_read,
+  .fs_close = grub_udf_close,
+  .fs_label = grub_udf_label,
+  .fs_uuid = grub_udf_uuid,
+#ifdef GRUB_UTIL
+  .reserved_first_sector = 1,
+  .blocklist_install = 1,
+#endif
+  .next = 0
+};
+
+GRUB_MOD_INIT (udf)
+{
+  grub_fs_register (&grub_udf_fs);
+  my_mod = mod;
+}
+
+GRUB_MOD_FINI (udf)
+{
+  grub_fs_unregister (&grub_udf_fs);
+}
diff --git a/GRUB2/grub-2.04/grub-core/kern/file.c b/GRUB2/grub-2.04/grub-core/kern/file.c
new file mode 100644 (file)
index 0000000..b01bfad
--- /dev/null
@@ -0,0 +1,256 @@
+/* file.c - file I/O functions */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2002,2006,2007,2009  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/misc.h>
+#include <grub/err.h>
+#include <grub/file.h>
+#include <grub/net.h>
+#include <grub/mm.h>
+#include <grub/fs.h>
+#include <grub/device.h>
+#include <grub/i18n.h>
+
+void (*EXPORT_VAR (grub_grubnet_fini)) (void);
+
+grub_file_filter_t grub_file_filters[GRUB_FILE_FILTER_MAX];
+
+/* Get the device part of the filename NAME. It is enclosed by parentheses.  */
+char *
+grub_file_get_device_name (const char *name)
+{
+  if (name[0] == '(')
+    {
+      char *p = grub_strchr (name, ')');
+      char *ret;
+
+      if (! p)
+       {
+         grub_error (GRUB_ERR_BAD_FILENAME, N_("missing `%c' symbol"), ')');
+         return 0;
+       }
+
+      ret = (char *) grub_malloc (p - name);
+      if (! ret)
+       return 0;
+
+      grub_memcpy (ret, name + 1, p - name - 1);
+      ret[p - name - 1] = '\0';
+      return ret;
+    }
+
+  return 0;
+}
+
+/* Support mem:xxx:size:xxx format in chainloader */
+grub_file_t grub_memfile_open(const char *name);
+#define GRUB_MEMFILE_MEM     "mem:"
+#define GRUB_MEMFILE_SIZE    "size:"
+
+grub_file_t grub_memfile_open(const char *name)
+{
+    char *size = NULL;
+    grub_file_t file = 0;
+    
+    file = (grub_file_t)grub_zalloc(sizeof(*file));
+    if (NULL == file)
+    {
+        return 0;
+    }
+
+    file->name = grub_strdup(name);
+    file->data = (void *)grub_strtoul(name + grub_strlen(GRUB_MEMFILE_MEM), NULL, 0);
+
+    size = grub_strstr(name, GRUB_MEMFILE_SIZE);
+    file->size = (grub_off_t)grub_strtoul(size + grub_strlen(GRUB_MEMFILE_SIZE), NULL, 0);
+
+    grub_errno = GRUB_ERR_NONE;
+    return file;
+}
+
+grub_file_t
+grub_file_open (const char *name, enum grub_file_type type)
+{
+  grub_device_t device = 0;
+  grub_file_t file = 0, last_file = 0;
+  char *device_name;
+  const char *file_name;
+  grub_file_filter_id_t filter;
+
+  /* <DESC> : mem:xxx:size:xxx format in chainloader */
+  if (grub_strncmp(name, GRUB_MEMFILE_MEM, grub_strlen(GRUB_MEMFILE_MEM)) == 0) {
+      return grub_memfile_open(name);
+  }  
+
+  device_name = grub_file_get_device_name (name);
+  if (grub_errno)
+    goto fail;
+
+  /* Get the file part of NAME.  */
+  file_name = (name[0] == '(') ? grub_strchr (name, ')') : NULL;
+  if (file_name)
+    file_name++;
+  else
+    file_name = name;
+
+  device = grub_device_open (device_name);
+  grub_free (device_name);
+  if (! device)
+    goto fail;
+
+  file = (grub_file_t) grub_zalloc (sizeof (*file));
+  if (! file)
+    goto fail;
+
+  file->device = device;
+
+  /* In case of relative pathnames and non-Unix systems (like Windows)
+   * name of host files may not start with `/'. Blocklists for host files
+   * are meaningless as well (for a start, host disk does not allow any direct
+   * access - it is just a marker). So skip host disk in this case.
+   */
+  if (device->disk && file_name[0] != '/'
+#if defined(GRUB_UTIL) || defined(GRUB_MACHINE_EMU)
+      && grub_strcmp (device->disk->name, "host")
+#endif
+     )
+    /* This is a block list.  */
+    file->fs = &grub_fs_blocklist;
+  else
+    {
+      file->fs = grub_fs_probe (device);
+      if (! file->fs)
+       goto fail;
+    }
+
+  if ((file->fs->fs_open) (file, file_name) != GRUB_ERR_NONE)
+    goto fail;
+
+  file->name = grub_strdup (name);
+  grub_errno = GRUB_ERR_NONE;
+
+  for (filter = 0; file && filter < ARRAY_SIZE (grub_file_filters);
+       filter++)
+    if (grub_file_filters[filter])
+      {
+       last_file = file;
+       file = grub_file_filters[filter] (file, type);
+       if (file && file != last_file)
+         {
+           file->name = grub_strdup (name);
+           grub_errno = GRUB_ERR_NONE;
+         }
+      }
+  if (!file)
+    grub_file_close (last_file);
+
+  return file;
+
+ fail:
+  if (device)
+    grub_device_close (device);
+
+  /* if (net) grub_net_close (net);  */
+
+  grub_free (file);
+
+  return 0;
+}
+
+grub_disk_read_hook_t grub_file_progress_hook;
+
+grub_ssize_t
+grub_file_read (grub_file_t file, void *buf, grub_size_t len)
+{
+  grub_ssize_t res;
+  grub_disk_read_hook_t read_hook;
+  void *read_hook_data;
+
+  if (file->offset > file->size)
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE,
+                 N_("attempt to read past the end of file"));
+      return -1;
+    }
+
+  if (len == 0)
+    return 0;
+
+  if (len > file->size - file->offset)
+    len = file->size - file->offset;
+
+  /* Prevent an overflow.  */
+  if ((grub_ssize_t) len < 0)
+    len >>= 1;
+
+  if (len == 0)
+    return 0;
+
+  if (grub_strncmp(file->name, GRUB_MEMFILE_MEM, grub_strlen(GRUB_MEMFILE_MEM)) == 0) {
+      grub_memcpy(buf, (grub_uint8_t *)(file->data) + file->offset, len);
+      file->offset += len;
+      return len;
+  }
+  
+  read_hook = file->read_hook;
+  read_hook_data = file->read_hook_data;
+  if (!file->read_hook)
+    {
+      file->read_hook = grub_file_progress_hook;
+      file->read_hook_data = file;
+      file->progress_offset = file->offset;
+    }
+  res = (file->fs->fs_read) (file, buf, len);
+  file->read_hook = read_hook;
+  file->read_hook_data = read_hook_data;
+  if (res > 0)
+    file->offset += res;
+
+  return res;
+}
+
+grub_err_t
+grub_file_close (grub_file_t file)
+{
+  if (file->fs && file->fs->fs_close)
+    (file->fs->fs_close) (file);
+
+  if (file->device)
+    grub_device_close (file->device);
+  grub_free (file->name);
+  grub_free (file);
+  return grub_errno;
+}
+
+grub_off_t
+grub_file_seek (grub_file_t file, grub_off_t offset)
+{
+  grub_off_t old;
+
+  if (offset > file->size)
+    {
+      grub_error (GRUB_ERR_OUT_OF_RANGE,
+                 N_("attempt to seek outside of the file"));
+      return -1;
+    }
+  
+  old = file->offset;
+  file->offset = offset;
+    
+  return old;
+}
diff --git a/GRUB2/grub-2.04/grub-core/kern/fs.c b/GRUB2/grub-2.04/grub-core/kern/fs.c
new file mode 100644 (file)
index 0000000..3052ae9
--- /dev/null
@@ -0,0 +1,304 @@
+/* fs.c - filesystem manager */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2002,2005,2007  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/disk.h>
+#include <grub/net.h>
+#include <grub/fs.h>
+#include <grub/file.h>
+#include <grub/err.h>
+#include <grub/misc.h>
+#include <grub/types.h>
+#include <grub/mm.h>
+#include <grub/term.h>
+#include <grub/i18n.h>
+#include <grub/env.h>
+
+grub_fs_t grub_fs_list = 0;
+
+grub_fs_autoload_hook_t grub_fs_autoload_hook = 0;
+
+/* Helper for grub_fs_probe.  */
+static int
+probe_dummy_iter (const char *filename __attribute__ ((unused)),
+                 const struct grub_dirhook_info *info __attribute__ ((unused)),
+                 void *data __attribute__ ((unused)))
+{
+  return 1;
+}
+
+grub_fs_t
+grub_fs_probe (grub_device_t device)
+{
+  grub_fs_t p;
+  const char *first_probe;
+  grub_size_t len;
+
+  if (device->disk)
+    {
+      /* Make it sure not to have an infinite recursive calls.  */
+      static int count = 0;
+
+      first_probe = grub_env_get("ventoy_fs_probe");
+      if (!first_probe)
+      {
+          first_probe = "iso9660";
+      }
+      len = grub_strlen(first_probe);
+
+      /* use iso9660 first */
+      for (p = grub_fs_list; p; p = p->next)
+      {
+          if (grub_strncmp(p->name, first_probe, len) == 0)
+          {
+              break;
+          }
+      }
+
+      if (p)
+      {
+         grub_dprintf ("fs", "Detecting %s...\n", p->name);
+
+         /* This is evil: newly-created just mounted BtrFS after copying all
+            GRUB files has a very peculiar unrecoverable corruption which
+            will be fixed at sync but we'd rather not do a global sync and
+            syncing just files doesn't seem to help. Relax the check for
+            this time.  */
+#ifdef GRUB_UTIL
+         if (grub_strcmp (p->name, "btrfs") == 0)
+           {
+             char *label = 0;
+             p->fs_uuid (device, &label);
+             if (label)
+               grub_free (label);
+           }
+         else
+#endif
+           (p->fs_dir) (device, "/", probe_dummy_iter, NULL);
+         if (grub_errno == GRUB_ERR_NONE)
+           return p;
+
+         grub_error_push ();
+         grub_dprintf ("fs", "%s detection failed.\n", p->name);
+         grub_error_pop ();
+
+         if (grub_errno != GRUB_ERR_BAD_FS
+             && grub_errno != GRUB_ERR_OUT_OF_RANGE)
+           return 0;
+
+         grub_errno = GRUB_ERR_NONE;
+       }
+
+      for (p = grub_fs_list; p; p = p->next)
+       {
+         grub_dprintf ("fs", "Detecting %s...\n", p->name);
+
+         /* This is evil: newly-created just mounted BtrFS after copying all
+            GRUB files has a very peculiar unrecoverable corruption which
+            will be fixed at sync but we'd rather not do a global sync and
+            syncing just files doesn't seem to help. Relax the check for
+            this time.  */
+#ifdef GRUB_UTIL
+         if (grub_strcmp (p->name, "btrfs") == 0)
+           {
+             char *label = 0;
+             p->fs_uuid (device, &label);
+             if (label)
+               grub_free (label);
+           }
+         else
+#endif
+           (p->fs_dir) (device, "/", probe_dummy_iter, NULL);
+         if (grub_errno == GRUB_ERR_NONE)
+           return p;
+
+         grub_error_push ();
+         grub_dprintf ("fs", "%s detection failed.\n", p->name);
+         grub_error_pop ();
+
+         if (grub_errno != GRUB_ERR_BAD_FS
+             && grub_errno != GRUB_ERR_OUT_OF_RANGE)
+           return 0;
+
+         grub_errno = GRUB_ERR_NONE;
+       }
+
+      /* Let's load modules automatically.  */
+      if (grub_fs_autoload_hook && count == 0)
+       {
+         count++;
+
+         while (grub_fs_autoload_hook ())
+           {
+             p = grub_fs_list;
+
+             (p->fs_dir) (device, "/", probe_dummy_iter, NULL);
+             if (grub_errno == GRUB_ERR_NONE)
+               {
+                 count--;
+                 return p;
+               }
+
+             if (grub_errno != GRUB_ERR_BAD_FS
+                 && grub_errno != GRUB_ERR_OUT_OF_RANGE)
+               {
+                 count--;
+                 return 0;
+               }
+
+             grub_errno = GRUB_ERR_NONE;
+           }
+
+         count--;
+       }
+    }
+  else if (device->net && device->net->fs)
+    return device->net->fs;
+
+  grub_error (GRUB_ERR_UNKNOWN_FS, N_("unknown filesystem"));
+  return 0;
+}
+
+\f
+
+/* Block list support routines.  */
+
+struct grub_fs_block
+{
+  grub_disk_addr_t offset;
+  unsigned long length;
+};
+
+static grub_err_t
+grub_fs_blocklist_open (grub_file_t file, const char *name)
+{
+  char *p = (char *) name;
+  unsigned num = 0;
+  unsigned i;
+  grub_disk_t disk = file->device->disk;
+  struct grub_fs_block *blocks;
+
+  /* First, count the number of blocks.  */
+  do
+    {
+      num++;
+      p = grub_strchr (p, ',');
+      if (p)
+       p++;
+    }
+  while (p);
+
+  /* Allocate a block list.  */
+  blocks = grub_zalloc (sizeof (struct grub_fs_block) * (num + 1));
+  if (! blocks)
+    return 0;
+
+  file->size = 0;
+  p = (char *) name;
+  for (i = 0; i < num; i++)
+    {
+      if (*p != '+')
+       {
+         blocks[i].offset = grub_strtoull (p, &p, 0);
+         if (grub_errno != GRUB_ERR_NONE || *p != '+')
+           {
+             grub_error (GRUB_ERR_BAD_FILENAME,
+                         N_("invalid file name `%s'"), name);
+             goto fail;
+           }
+       }
+
+      p++;
+      blocks[i].length = grub_strtoul (p, &p, 0);
+      if (grub_errno != GRUB_ERR_NONE
+         || blocks[i].length == 0
+         || (*p && *p != ',' && ! grub_isspace (*p)))
+       {
+         grub_error (GRUB_ERR_BAD_FILENAME,
+                     N_("invalid file name `%s'"), name);
+         goto fail;
+       }
+
+      if (disk->total_sectors < blocks[i].offset + blocks[i].length)
+       {
+         grub_error (GRUB_ERR_BAD_FILENAME, "beyond the total sectors");
+         goto fail;
+       }
+
+      file->size += (blocks[i].length << GRUB_DISK_SECTOR_BITS);
+      p++;
+    }
+
+  file->data = blocks;
+
+  return GRUB_ERR_NONE;
+
+ fail:
+  grub_free (blocks);
+  return grub_errno;
+}
+
+static grub_ssize_t
+grub_fs_blocklist_read (grub_file_t file, char *buf, grub_size_t len)
+{
+  struct grub_fs_block *p;
+  grub_disk_addr_t sector;
+  grub_off_t offset;
+  grub_ssize_t ret = 0;
+
+  if (len > file->size - file->offset)
+    len = file->size - file->offset;
+
+  sector = (file->offset >> GRUB_DISK_SECTOR_BITS);
+  offset = (file->offset & (GRUB_DISK_SECTOR_SIZE - 1));
+  for (p = file->data; p->length && len > 0; p++)
+    {
+      if (sector < p->length)
+       {
+         grub_size_t size;
+
+         size = len;
+         if (((size + offset + GRUB_DISK_SECTOR_SIZE - 1)
+              >> GRUB_DISK_SECTOR_BITS) > p->length - sector)
+           size = ((p->length - sector) << GRUB_DISK_SECTOR_BITS) - offset;
+
+         if (grub_disk_read (file->device->disk, p->offset + sector, offset,
+                             size, buf) != GRUB_ERR_NONE)
+           return -1;
+
+         ret += size;
+         len -= size;
+         sector -= ((size + offset) >> GRUB_DISK_SECTOR_BITS);
+         offset = ((size + offset) & (GRUB_DISK_SECTOR_SIZE - 1));
+       }
+      else
+       sector -= p->length;
+    }
+
+  return ret;
+}
+
+struct grub_fs grub_fs_blocklist =
+  {
+    .name = "blocklist",
+    .fs_dir = 0,
+    .fs_open = grub_fs_blocklist_open,
+    .fs_read = grub_fs_blocklist_read,
+    .fs_close = 0,
+    .next = 0
+  };
diff --git a/GRUB2/grub-2.04/grub-core/kern/main.c b/GRUB2/grub-2.04/grub-core/kern/main.c
new file mode 100644 (file)
index 0000000..43b2de9
--- /dev/null
@@ -0,0 +1,312 @@
+/* main.c - the kernel main routine */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2002,2003,2005,2006,2008,2009  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/kernel.h>
+#include <grub/misc.h>
+#include <grub/symbol.h>
+#include <grub/dl.h>
+#include <grub/term.h>
+#include <grub/file.h>
+#include <grub/device.h>
+#include <grub/env.h>
+#include <grub/mm.h>
+#include <grub/command.h>
+#include <grub/reader.h>
+#include <grub/parser.h>
+
+#ifdef GRUB_MACHINE_PCBIOS
+#include <grub/machine/memory.h>
+#endif
+
+grub_addr_t
+grub_modules_get_end (void)
+{
+  struct grub_module_info *modinfo;
+
+  modinfo = (struct grub_module_info *) grub_modbase;
+
+  /* Check if there are any modules.  */
+  if ((modinfo == 0) || modinfo->magic != GRUB_MODULE_MAGIC)
+    return grub_modbase;
+
+  return grub_modbase + modinfo->size;
+}
+
+/* Load all modules in core.  */
+static void
+grub_load_modules (void)
+{
+  struct grub_module_header *header;
+  FOR_MODULES (header)
+  {
+    /* Not an ELF module, skip.  */
+    if (header->type != OBJ_TYPE_ELF)
+      continue;
+
+    if (! grub_dl_load_core ((char *) header + sizeof (struct grub_module_header),
+                            (header->size - sizeof (struct grub_module_header))))
+      grub_fatal ("%s", grub_errmsg);
+
+    if (grub_errno)
+      grub_print_error ();
+  }
+}
+
+static char *load_config;
+
+static void
+grub_load_config (void)
+{
+  struct grub_module_header *header;
+  FOR_MODULES (header)
+  {
+    /* Not an embedded config, skip.  */
+    if (header->type != OBJ_TYPE_CONFIG)
+      continue;
+
+    load_config = grub_malloc (header->size - sizeof (struct grub_module_header) + 1);
+    if (!load_config)
+      {
+       grub_print_error ();
+       break;
+      }
+    grub_memcpy (load_config, (char *) header +
+                sizeof (struct grub_module_header),
+                header->size - sizeof (struct grub_module_header));
+    load_config[header->size - sizeof (struct grub_module_header)] = 0;
+    break;
+  }
+}
+
+/* Write hook for the environment variables of root. Remove surrounding
+   parentheses, if any.  */
+static char *
+grub_env_write_root (struct grub_env_var *var __attribute__ ((unused)),
+                    const char *val)
+{
+  /* XXX Is it better to check the existence of the device?  */
+  grub_size_t len = grub_strlen (val);
+
+  if (val[0] == '(' && val[len - 1] == ')')
+    return grub_strndup (val + 1, len - 2);
+
+  return grub_strdup (val);
+}
+
+static void
+grub_set_prefix_and_root (void)
+{
+  char *device = NULL;
+  char *path = NULL;
+  char *fwdevice = NULL;
+  char *fwpath = NULL;
+  char *prefix = NULL;
+  struct grub_module_header *header;
+
+  FOR_MODULES (header)
+    if (header->type == OBJ_TYPE_PREFIX)
+      prefix = (char *) header + sizeof (struct grub_module_header);
+
+  grub_register_variable_hook ("root", 0, grub_env_write_root);
+
+  grub_machine_get_bootlocation (&fwdevice, &fwpath);
+
+  if (fwdevice)
+    {
+      char *cmdpath;
+
+      cmdpath = grub_xasprintf ("(%s)%s", fwdevice, fwpath ? : "");
+      if (cmdpath)
+       {
+         grub_env_set ("cmdpath", cmdpath);
+         grub_env_export ("cmdpath");
+         grub_free (cmdpath);
+       }
+    }
+
+  if (prefix)
+    {
+      char *pptr = NULL;
+      if (prefix[0] == '(')
+       {
+         pptr = grub_strrchr (prefix, ')');
+         if (pptr)
+           {
+             device = grub_strndup (prefix + 1, pptr - prefix - 1);
+             pptr++;
+           }
+       }
+      if (!pptr)
+       pptr = prefix;
+      if (pptr[0])
+       path = grub_strdup (pptr);
+    }
+
+  if (!device && fwdevice)
+    device = fwdevice;
+  else if (fwdevice && (device[0] == ',' || !device[0]))
+    {
+      /* We have a partition, but still need to fill in the drive.  */
+      char *comma, *new_device;
+
+      for (comma = fwdevice; *comma; )
+       {
+         if (comma[0] == '\\' && comma[1] == ',')
+           {
+             comma += 2;
+             continue;
+           }
+         if (*comma == ',')
+           break;
+         comma++;
+       }
+      if (*comma)
+       {
+         char *drive = grub_strndup (fwdevice, comma - fwdevice);
+         new_device = grub_xasprintf ("%s%s", drive, device);
+         grub_free (drive);
+       }
+      else
+       new_device = grub_xasprintf ("%s%s", fwdevice, device);
+
+      grub_free (fwdevice);
+      grub_free (device);
+      device = new_device;
+    }
+  else
+    grub_free (fwdevice);
+  if (fwpath && !path)
+    {
+      grub_size_t len = grub_strlen (fwpath);
+      while (len > 1 && fwpath[len - 1] == '/')
+       fwpath[--len] = 0;
+      if (len >= sizeof (GRUB_TARGET_CPU "-" GRUB_PLATFORM) - 1
+         && grub_memcmp (fwpath + len - (sizeof (GRUB_TARGET_CPU "-" GRUB_PLATFORM) - 1), GRUB_TARGET_CPU "-" GRUB_PLATFORM,
+                         sizeof (GRUB_TARGET_CPU "-" GRUB_PLATFORM) - 1) == 0)
+       fwpath[len - (sizeof (GRUB_TARGET_CPU "-" GRUB_PLATFORM) - 1)] = 0;
+      path = fwpath;
+    }
+  else
+    grub_free (fwpath);
+  if (device)
+    {
+      char *prefix_set;
+    
+      prefix_set = grub_xasprintf ("(%s)%s", device, path ? : "");
+      if (prefix_set)
+       {
+         grub_env_set ("prefix", prefix_set);
+         grub_free (prefix_set);
+       }
+      grub_env_set ("root", device);
+    }
+
+  grub_free (device);
+  grub_free (path);
+  grub_print_error ();
+}
+
+/* Load the normal mode module and execute the normal mode if possible.  */
+static void
+grub_load_normal_mode (void)
+{
+  /* Load the module.  */
+  grub_dl_load ("normal");
+
+  /* Print errors if any.  */
+  grub_print_error ();
+  grub_errno = 0;
+
+  grub_command_execute ("normal", 0, 0);
+}
+
+static void
+reclaim_module_space (void)
+{
+  grub_addr_t modstart, modend;
+
+  if (!grub_modbase)
+    return;
+
+#ifdef GRUB_MACHINE_PCBIOS
+  modstart = GRUB_MEMORY_MACHINE_DECOMPRESSION_ADDR;
+#else
+  modstart = grub_modbase;
+#endif
+  modend = grub_modules_get_end ();
+  grub_modbase = 0;
+
+#if GRUB_KERNEL_PRELOAD_SPACE_REUSABLE
+  grub_mm_init_region ((void *) modstart, modend - modstart);
+#else
+  (void) modstart;
+  (void) modend;
+#endif
+}
+
+/* The main routine.  */
+void __attribute__ ((noreturn))
+grub_main (void)
+{
+  /* First of all, initialize the machine.  */
+  grub_machine_init ();
+
+  grub_boot_time ("After machine init.");
+
+  /* Hello.  */
+  grub_setcolorstate (GRUB_TERM_COLOR_HIGHLIGHT);
+  //grub_printf ("Welcome to GRUB!\n\n");
+  grub_setcolorstate (GRUB_TERM_COLOR_STANDARD);
+
+  grub_load_config ();
+
+  grub_boot_time ("Before loading embedded modules.");
+
+  /* Load pre-loaded modules and free the space.  */
+  grub_register_exported_symbols ();
+#ifdef GRUB_LINKER_HAVE_INIT
+  grub_arch_dl_init_linker ();
+#endif  
+  grub_load_modules ();
+
+  grub_boot_time ("After loading embedded modules.");
+
+  /* It is better to set the root device as soon as possible,
+     for convenience.  */
+  grub_set_prefix_and_root ();
+  grub_env_export ("root");
+  grub_env_export ("prefix");
+
+  /* Reclaim space used for modules.  */
+  reclaim_module_space ();
+
+  grub_boot_time ("After reclaiming module space.");
+
+  grub_register_core_commands ();
+
+  grub_boot_time ("Before execution of embedded config.");
+
+  if (load_config)
+    grub_parser_execute (load_config);
+
+  grub_boot_time ("After execution of embedded config. Attempt to go to normal mode");
+
+  grub_load_normal_mode ();
+  grub_rescue_run ();
+}
diff --git a/GRUB2/grub-2.04/grub-core/lib/cmdline.c b/GRUB2/grub-2.04/grub-core/lib/cmdline.c
new file mode 100644 (file)
index 0000000..cc87e16
--- /dev/null
@@ -0,0 +1,118 @@
+/* cmdline.c - linux command line handling */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2010  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/lib/cmdline.h>
+#include <grub/misc.h>
+
+static unsigned int check_arg (char *c, int *has_space)
+{
+  int space = 0;
+  unsigned int size = 0;
+
+  while (*c)
+    {
+      if (*c == '\\' || *c == '\'' || *c == '"')
+       size++;
+      else if (*c == ' ')
+       space = 1;
+
+      size++;
+      c++;
+    }
+
+  if (space)
+    size += 2;
+
+  if (has_space)
+    *has_space = space;
+
+  return size;
+}
+
+unsigned int grub_loader_cmdline_size (int argc, char *argv[])
+{
+  int i;
+  unsigned int size = 0;
+
+  for (i = 0; i < argc; i++)
+    {
+      size += check_arg (argv[i], 0);
+      size++; /* Separator space or NULL.  */
+    }
+
+  if (size == 0)
+    size = 1;
+
+  return size;
+}
+
+grub_err_t
+grub_create_loader_cmdline (int argc, char *argv[], char *buf,
+                           grub_size_t size, enum grub_verify_string_type type)
+{
+  int i, space;
+  unsigned int arg_size;
+  char *c, *orig_buf = buf;
+
+  for (i = 0; i < argc; i++)
+    {
+      c = argv[i];
+      arg_size = check_arg(argv[i], &space);
+      arg_size++; /* Separator space or NULL.  */
+
+      if (size < arg_size)
+       break;
+
+      size -= arg_size;
+
+      if (space)
+       *buf++ = '"';
+
+      while (*c)
+       {
+         if (*c == '\\' && *(c+1) == 'x' &&
+                  grub_isxdigit(*(c+2)) && grub_isxdigit(*(c+3)))
+           {
+             *buf++ = *c++;
+             *buf++ = *c++;
+             *buf++ = *c++;
+             *buf++ = *c++;
+             continue;
+           }
+         else if (*c == '\\' || *c == '\'' || *c == '"')
+           *buf++ = '\\';
+
+         *buf++ = *c;
+         c++;
+       }
+
+      if (space)
+       *buf++ = '"';
+
+      *buf++ = ' ';
+    }
+
+  /* Replace last space with null.  */
+  if (i)
+    buf--;
+
+  *buf = 0;
+
+  return grub_verify_string (orig_buf, type);
+}
diff --git a/GRUB2/grub-2.04/grub-core/loader/efi/chainloader.c b/GRUB2/grub-2.04/grub-core/loader/efi/chainloader.c
new file mode 100644 (file)
index 0000000..fa8a4b9
--- /dev/null
@@ -0,0 +1,428 @@
+/* chainloader.c - boot another boot loader */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2002,2004,2006,2007,2008  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+/* TODO: support load options.  */
+
+#include <grub/loader.h>
+#include <grub/file.h>
+#include <grub/err.h>
+#include <grub/device.h>
+#include <grub/disk.h>
+#include <grub/misc.h>
+#include <grub/charset.h>
+#include <grub/mm.h>
+#include <grub/types.h>
+#include <grub/dl.h>
+#include <grub/efi/api.h>
+#include <grub/efi/efi.h>
+#include <grub/efi/disk.h>
+#include <grub/command.h>
+#include <grub/i18n.h>
+#include <grub/net.h>
+#if defined (__i386__) || defined (__x86_64__)
+#include <grub/macho.h>
+#include <grub/i386/macho.h>
+#endif
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static grub_dl_t my_mod;
+
+static grub_efi_physical_address_t address;
+static grub_efi_uintn_t pages;
+static grub_efi_device_path_t *file_path;
+static grub_efi_handle_t image_handle;
+static grub_efi_char16_t *cmdline;
+
+static grub_err_t
+grub_chainloader_unload (void)
+{
+  grub_efi_boot_services_t *b;
+
+  b = grub_efi_system_table->boot_services;
+  efi_call_1 (b->unload_image, image_handle);
+  efi_call_2 (b->free_pages, address, pages);
+
+  grub_free (file_path);
+  grub_free (cmdline);
+  cmdline = 0;
+  file_path = 0;
+
+  grub_dl_unref (my_mod);
+  return GRUB_ERR_NONE;
+}
+
+static grub_err_t
+grub_chainloader_boot (void)
+{
+  grub_efi_boot_services_t *b;
+  grub_efi_status_t status;
+  grub_efi_uintn_t exit_data_size;
+  grub_efi_char16_t *exit_data = NULL;
+
+  b = grub_efi_system_table->boot_services;
+  status = efi_call_3 (b->start_image, image_handle, &exit_data_size, &exit_data);
+  if (status != GRUB_EFI_SUCCESS)
+    {
+      if (exit_data)
+       {
+         char *buf;
+
+         buf = grub_malloc (exit_data_size * 4 + 1);
+         if (buf)
+           {
+             *grub_utf16_to_utf8 ((grub_uint8_t *) buf,
+                                  exit_data, exit_data_size) = 0;
+
+             grub_error (GRUB_ERR_BAD_OS, buf);
+             grub_free (buf);
+           }
+       }
+      else
+       grub_error (GRUB_ERR_BAD_OS, "unknown error");
+    }
+
+  if (exit_data)
+    efi_call_1 (b->free_pool, exit_data);
+
+  grub_loader_unset ();
+
+  return grub_errno;
+}
+
+static void
+copy_file_path (grub_efi_file_path_device_path_t *fp,
+               const char *str, grub_efi_uint16_t len)
+{
+  grub_efi_char16_t *p, *path_name;
+  grub_efi_uint16_t size;
+
+  fp->header.type = GRUB_EFI_MEDIA_DEVICE_PATH_TYPE;
+  fp->header.subtype = GRUB_EFI_FILE_PATH_DEVICE_PATH_SUBTYPE;
+
+  path_name = grub_malloc (len * GRUB_MAX_UTF16_PER_UTF8 * sizeof (*path_name));
+  if (!path_name)
+    return;
+
+  size = grub_utf8_to_utf16 (path_name, len * GRUB_MAX_UTF16_PER_UTF8,
+                            (const grub_uint8_t *) str, len, 0);
+  for (p = path_name; p < path_name + size; p++)
+    if (*p == '/')
+      *p = '\\';
+
+  grub_memcpy (fp->path_name, path_name, size * sizeof (*fp->path_name));
+  /* File Path is NULL terminated */
+  fp->path_name[size++] = '\0';
+  fp->header.length = size * sizeof (grub_efi_char16_t) + sizeof (*fp);
+  grub_free (path_name);
+}
+
+static grub_efi_device_path_t *
+make_file_path (grub_efi_device_path_t *dp, const char *filename)
+{
+  char *dir_start;
+  char *dir_end;
+  grub_size_t size;
+  grub_efi_device_path_t *d;
+
+  dir_start = grub_strchr (filename, ')');
+  if (! dir_start)
+    dir_start = (char *) filename;
+  else
+    dir_start++;
+
+  dir_end = grub_strrchr (dir_start, '/');
+  if (! dir_end)
+    {
+      grub_error (GRUB_ERR_BAD_FILENAME, "invalid EFI file path");
+      return 0;
+    }
+
+  size = 0;
+  d = dp;
+  while (1)
+    {
+      size += GRUB_EFI_DEVICE_PATH_LENGTH (d);
+      if ((GRUB_EFI_END_ENTIRE_DEVICE_PATH (d)))
+       break;
+      d = GRUB_EFI_NEXT_DEVICE_PATH (d);
+    }
+
+  /* File Path is NULL terminated. Allocate space for 2 extra characters */
+  /* FIXME why we split path in two components? */
+  file_path = grub_malloc (size
+                          + ((grub_strlen (dir_start) + 2)
+                             * GRUB_MAX_UTF16_PER_UTF8
+                             * sizeof (grub_efi_char16_t))
+                          + sizeof (grub_efi_file_path_device_path_t) * 2);
+  if (! file_path)
+    return 0;
+
+  grub_memcpy (file_path, dp, size);
+
+  /* Fill the file path for the directory.  */
+  d = (grub_efi_device_path_t *) ((char *) file_path
+                                 + ((char *) d - (char *) dp));
+  //grub_efi_print_device_path (d);
+  copy_file_path ((grub_efi_file_path_device_path_t *) d,
+                 dir_start, dir_end - dir_start);
+
+  /* Fill the file path for the file.  */
+  d = GRUB_EFI_NEXT_DEVICE_PATH (d);
+  copy_file_path ((grub_efi_file_path_device_path_t *) d,
+                 dir_end + 1, grub_strlen (dir_end + 1));
+
+  /* Fill the end of device path nodes.  */
+  d = GRUB_EFI_NEXT_DEVICE_PATH (d);
+  d->type = GRUB_EFI_END_DEVICE_PATH_TYPE;
+  d->subtype = GRUB_EFI_END_ENTIRE_DEVICE_PATH_SUBTYPE;
+  d->length = sizeof (*d);
+
+  return file_path;
+}
+
+static grub_err_t
+grub_cmd_chainloader (grub_command_t cmd __attribute__ ((unused)),
+                     int argc, char *argv[])
+{
+  grub_file_t file = 0;
+  grub_ssize_t size;
+  grub_efi_status_t status;
+  grub_efi_boot_services_t *b;
+  grub_device_t dev = 0;
+  grub_efi_device_path_t *dp = 0;
+  grub_efi_loaded_image_t *loaded_image;
+  char *filename;
+  void *boot_image = 0;
+  grub_efi_handle_t dev_handle = 0;
+
+  if (argc == 0)
+    return grub_error (GRUB_ERR_BAD_ARGUMENT, N_("filename expected"));
+  filename = argv[0];
+
+  grub_dl_ref (my_mod);
+
+  /* Initialize some global variables.  */
+  address = 0;
+  image_handle = 0;
+  file_path = 0;
+
+  b = grub_efi_system_table->boot_services;
+
+  file = grub_file_open (filename, GRUB_FILE_TYPE_EFI_CHAINLOADED_IMAGE);
+  if (! file)
+    goto fail;
+
+  /* Get the root device's device path.  */
+  dev = grub_device_open (0);
+  if (! dev)
+    goto fail;
+
+  if (dev->disk)
+    dev_handle = grub_efidisk_get_device_handle (dev->disk);
+  else if (dev->net && dev->net->server)
+    {
+      grub_net_network_level_address_t addr;
+      struct grub_net_network_level_interface *inf;
+      grub_net_network_level_address_t gateway;
+      grub_err_t err;
+
+      err = grub_net_resolve_address (dev->net->server, &addr);
+      if (err)
+       goto fail;
+
+      err = grub_net_route_address (addr, &gateway, &inf);
+      if (err)
+       goto fail;
+
+      dev_handle = grub_efinet_get_device_handle (inf->card);
+    }
+
+  if (dev_handle)
+    dp = grub_efi_get_device_path (dev_handle);
+
+  if (! dp)
+    {
+      grub_error (GRUB_ERR_BAD_DEVICE, "not a valid root device");
+      goto fail;
+    }
+
+  file_path = make_file_path (dp, filename);
+  if (! file_path)
+    goto fail;
+
+  //grub_printf ("file path: ");
+  //grub_efi_print_device_path (file_path);
+
+  size = grub_file_size (file);
+  if (!size)
+    {
+      grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+                 filename);
+      goto fail;
+    }
+  pages = (((grub_efi_uintn_t) size + ((1 << 12) - 1)) >> 12);
+
+  status = efi_call_4 (b->allocate_pages, GRUB_EFI_ALLOCATE_ANY_PAGES,
+                             GRUB_EFI_LOADER_CODE,
+                             pages, &address);
+  if (status != GRUB_EFI_SUCCESS)
+    {
+      grub_dprintf ("chain", "Failed to allocate %u pages\n",
+                   (unsigned int) pages);
+      grub_error (GRUB_ERR_OUT_OF_MEMORY, N_("out of memory"));
+      goto fail;
+    }
+
+  boot_image = (void *) ((grub_addr_t) address);
+  if (grub_file_read (file, boot_image, size) != size)
+    {
+      if (grub_errno == GRUB_ERR_NONE)
+       grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+                   filename);
+
+      goto fail;
+    }
+
+#if defined (__i386__) || defined (__x86_64__)
+  if (size >= (grub_ssize_t) sizeof (struct grub_macho_fat_header))
+    {
+      struct grub_macho_fat_header *head = boot_image;
+      if (head->magic
+         == grub_cpu_to_le32_compile_time (GRUB_MACHO_FAT_EFI_MAGIC))
+       {
+         grub_uint32_t i;
+         struct grub_macho_fat_arch *archs
+           = (struct grub_macho_fat_arch *) (head + 1);
+         for (i = 0; i < grub_cpu_to_le32 (head->nfat_arch); i++)
+           {
+             if (GRUB_MACHO_CPUTYPE_IS_HOST_CURRENT (archs[i].cputype))
+               break;
+           }
+         if (i == grub_cpu_to_le32 (head->nfat_arch))
+           {
+             grub_error (GRUB_ERR_BAD_OS, "no compatible arch found");
+             goto fail;
+           }
+         if (grub_cpu_to_le32 (archs[i].offset)
+             > ~grub_cpu_to_le32 (archs[i].size)
+             || grub_cpu_to_le32 (archs[i].offset)
+             + grub_cpu_to_le32 (archs[i].size)
+             > (grub_size_t) size)
+           {
+             grub_error (GRUB_ERR_BAD_OS, N_("premature end of file %s"),
+                         filename);
+             goto fail;
+           }
+         boot_image = (char *) boot_image + grub_cpu_to_le32 (archs[i].offset);
+         size = grub_cpu_to_le32 (archs[i].size);
+       }
+    }
+#endif
+
+  status = efi_call_6 (b->load_image, 0, grub_efi_image_handle, file_path,
+                      boot_image, size,
+                      &image_handle);
+  if (status != GRUB_EFI_SUCCESS)
+    {
+      if (status == GRUB_EFI_OUT_OF_RESOURCES)
+       grub_error (GRUB_ERR_OUT_OF_MEMORY, "out of resources");
+      else
+       grub_error (GRUB_ERR_BAD_OS, "cannot load image");
+
+      goto fail;
+    }
+
+  /* LoadImage does not set a device handler when the image is
+     loaded from memory, so it is necessary to set it explicitly here.
+     This is a mess.  */
+  loaded_image = grub_efi_get_loaded_image (image_handle);
+  if (! loaded_image)
+    {
+      grub_error (GRUB_ERR_BAD_OS, "no loaded image available");
+      goto fail;
+    }
+  loaded_image->device_handle = dev_handle;
+
+  if (argc > 1)
+    {
+      int i, len;
+      grub_efi_char16_t *p16;
+
+      for (i = 1, len = 0; i < argc; i++)
+        len += grub_strlen (argv[i]) + 1;
+
+      len *= sizeof (grub_efi_char16_t);
+      cmdline = p16 = grub_malloc (len);
+      if (! cmdline)
+        goto fail;
+
+      for (i = 1; i < argc; i++)
+        {
+          char *p8;
+
+          p8 = argv[i];
+          while (*p8)
+            *(p16++) = *(p8++);
+
+          *(p16++) = ' ';
+        }
+      *(--p16) = 0;
+
+      loaded_image->load_options = cmdline;
+      loaded_image->load_options_size = len;
+    }
+
+  grub_file_close (file);
+  grub_device_close (dev);
+
+  grub_loader_set (grub_chainloader_boot, grub_chainloader_unload, 0);
+  return 0;
+
+ fail:
+
+  if (dev)
+    grub_device_close (dev);
+
+  if (file)
+    grub_file_close (file);
+
+  grub_free (file_path);
+
+  if (address)
+    efi_call_2 (b->free_pages, address, pages);
+
+  grub_dl_unref (my_mod);
+
+  return grub_errno;
+}
+
+static grub_command_t cmd;
+
+GRUB_MOD_INIT(chainloader)
+{
+  cmd = grub_register_command ("chainloader", grub_cmd_chainloader,
+                              0, N_("Load another boot loader."));
+  my_mod = mod;
+}
+
+GRUB_MOD_FINI(chainloader)
+{
+  grub_unregister_command (cmd);
+}
diff --git a/GRUB2/grub-2.04/grub-core/normal/menu.c b/GRUB2/grub-2.04/grub-core/normal/menu.c
new file mode 100644 (file)
index 0000000..a523181
--- /dev/null
@@ -0,0 +1,913 @@
+/* menu.c - General supporting functionality for menus.  */
+/*
+ *  GRUB  --  GRand Unified Bootloader
+ *  Copyright (C) 2003,2004,2005,2006,2007,2008,2009,2010  Free Software Foundation, Inc.
+ *
+ *  GRUB 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 3 of the License, or
+ *  (at your option) any later version.
+ *
+ *  GRUB 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 GRUB.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <grub/normal.h>
+#include <grub/misc.h>
+#include <grub/loader.h>
+#include <grub/mm.h>
+#include <grub/time.h>
+#include <grub/env.h>
+#include <grub/menu_viewer.h>
+#include <grub/command.h>
+#include <grub/parser.h>
+#include <grub/auth.h>
+#include <grub/i18n.h>
+#include <grub/term.h>
+#include <grub/script_sh.h>
+#include <grub/gfxterm.h>
+#include <grub/dl.h>
+
+/* Time to delay after displaying an error message about a default/fallback
+   entry failing to boot.  */
+#define DEFAULT_ENTRY_ERROR_DELAY_MS  2500
+
+grub_err_t (*grub_gfxmenu_try_hook) (int entry, grub_menu_t menu,
+                                    int nested) = NULL;
+
+enum timeout_style {
+  TIMEOUT_STYLE_MENU,
+  TIMEOUT_STYLE_COUNTDOWN,
+  TIMEOUT_STYLE_HIDDEN
+};
+
+struct timeout_style_name {
+  const char *name;
+  enum timeout_style style;
+} timeout_style_names[] = {
+  {"menu", TIMEOUT_STYLE_MENU},
+  {"countdown", TIMEOUT_STYLE_COUNTDOWN},
+  {"hidden", TIMEOUT_STYLE_HIDDEN},
+  {NULL, 0}
+};
+
+/* Wait until the user pushes any key so that the user
+   can see what happened.  */
+void
+grub_wait_after_message (void)
+{
+  grub_uint64_t endtime;
+  grub_xputs ("\n");
+  grub_printf_ (N_("Press any key to continue..."));
+  grub_refresh ();
+
+  endtime = grub_get_time_ms () + 10000;
+
+  while (grub_get_time_ms () < endtime
+        && grub_getkey_noblock () == GRUB_TERM_NO_KEY);
+
+  grub_xputs ("\n");
+}
+
+/* Get a menu entry by its index in the entry list.  */
+grub_menu_entry_t
+grub_menu_get_entry (grub_menu_t menu, int no)
+{
+  grub_menu_entry_t e;
+
+  for (e = menu->entry_list; e && no > 0; e = e->next, no--)
+    ;
+
+  return e;
+}
+
+/* Get the index of a menu entry associated with a given hotkey, or -1.  */
+static int
+get_entry_index_by_hotkey (grub_menu_t menu, int hotkey)
+{
+  grub_menu_entry_t entry;
+  int i;
+
+  for (i = 0, entry = menu->entry_list; i < menu->size;
+       i++, entry = entry->next)
+    if (entry->hotkey == hotkey)
+      return i;
+
+  return -1;
+}
+
+/* Return the timeout style.  If the variable "timeout_style" is not set or
+   invalid, default to TIMEOUT_STYLE_MENU.  */
+static enum timeout_style
+get_timeout_style (void)
+{
+  const char *val;
+  struct timeout_style_name *style_name;
+
+  val = grub_env_get ("timeout_style");
+  if (!val)
+    return TIMEOUT_STYLE_MENU;
+
+  for (style_name = timeout_style_names; style_name->name; style_name++)
+    if (grub_strcmp (style_name->name, val) == 0)
+      return style_name->style;
+
+  return TIMEOUT_STYLE_MENU;
+}
+
+/* Return the current timeout. If the variable "timeout" is not set or
+   invalid, return -1.  */
+int
+grub_menu_get_timeout (void)
+{
+  const char *val;
+  int timeout;
+
+  val = grub_env_get ("timeout");
+  if (! val)
+    return -1;
+
+  grub_error_push ();
+
+  timeout = (int) grub_strtoul (val, 0, 0);
+
+  /* If the value is invalid, unset the variable.  */
+  if (grub_errno != GRUB_ERR_NONE)
+    {
+      grub_env_unset ("timeout");
+      grub_errno = GRUB_ERR_NONE;
+      timeout = -1;
+    }
+
+  grub_error_pop ();
+
+  return timeout;
+}
+
+/* Set current timeout in the variable "timeout".  */
+void
+grub_menu_set_timeout (int timeout)
+{
+  /* Ignore TIMEOUT if it is zero, because it will be unset really soon.  */
+  if (timeout > 0)
+    {
+      char buf[16];
+
+      grub_snprintf (buf, sizeof (buf), "%d", timeout);
+      grub_env_set ("timeout", buf);
+    }
+}
+
+/* Get the first entry number from the value of the environment variable NAME,
+   which is a space-separated list of non-negative integers.  The entry number
+   which is returned is stripped from the value of NAME.  If no entry number
+   can be found, -1 is returned.  */
+static int
+get_and_remove_first_entry_number (const char *name)
+{
+  const char *val;
+  char *tail;
+  int entry;
+
+  val = grub_env_get (name);
+  if (! val)
+    return -1;
+
+  grub_error_push ();
+
+  entry = (int) grub_strtoul (val, &tail, 0);
+
+  if (grub_errno == GRUB_ERR_NONE)
+    {
+      /* Skip whitespace to find the next digit.  */
+      while (*tail && grub_isspace (*tail))
+       tail++;
+      grub_env_set (name, tail);
+    }
+  else
+    {
+      grub_env_unset (name);
+      grub_errno = GRUB_ERR_NONE;
+      entry = -1;
+    }
+
+  grub_error_pop ();
+
+  return entry;
+}
+
+/* Run a menu entry.  */
+static void
+grub_menu_execute_entry(grub_menu_entry_t entry, int auto_boot)
+{
+  grub_err_t err = GRUB_ERR_NONE;
+  int errs_before;
+  grub_menu_t menu = NULL;
+  char *optr, *buf, *oldchosen = NULL, *olddefault = NULL;
+  const char *ptr, *chosen, *def;
+  grub_size_t sz = 0;
+
+  if (entry->restricted)
+    err = grub_auth_check_authentication (entry->users);
+
+  if (err)
+    {
+      grub_print_error ();
+      grub_errno = GRUB_ERR_NONE;
+      return;
+    }
+
+  errs_before = grub_err_printed_errors;
+
+  chosen = grub_env_get ("chosen");
+  def = grub_env_get ("default");
+
+  if (entry->submenu)
+    {
+      grub_env_context_open ();
+      menu = grub_zalloc (sizeof (*menu));
+      if (! menu)
+       return;
+      grub_env_set_menu (menu);
+      if (auto_boot)
+       grub_env_set ("timeout", "0");
+    }
+
+  for (ptr = entry->id; *ptr; ptr++)
+    sz += (*ptr == '>') ? 2 : 1;
+  if (chosen)
+    {
+      oldchosen = grub_strdup (chosen);
+      if (!oldchosen)
+       grub_print_error ();
+    }
+  if (def)
+    {
+      olddefault = grub_strdup (def);
+      if (!olddefault)
+       grub_print_error ();
+    }
+  sz++;
+  if (chosen)
+    sz += grub_strlen (chosen);
+  sz++;
+  buf = grub_malloc (sz);
+  if (!buf)
+    grub_print_error ();
+  else
+    {
+      optr = buf;
+      if (chosen)
+       {
+         optr = grub_stpcpy (optr, chosen);
+         *optr++ = '>';
+       }
+      for (ptr = entry->id; *ptr; ptr++)
+       {
+         if (*ptr == '>')
+           *optr++ = '>';
+         *optr++ = *ptr;
+       }
+      *optr = 0;
+      grub_env_set ("chosen", buf);
+      grub_env_export ("chosen");
+      grub_free (buf);
+    }
+
+  for (ptr = def; ptr && *ptr; ptr++)
+    {
+      if (ptr[0] == '>' && ptr[1] == '>')
+       {
+         ptr++;
+         continue;
+       }
+      if (ptr[0] == '>')
+       break;
+    }
+
+  if (ptr && ptr[0] && ptr[1])
+    grub_env_set ("default", ptr + 1);
+  else
+    grub_env_unset ("default");
+
+  grub_script_execute_new_scope (entry->sourcecode, entry->argc, entry->args);
+
+  if (errs_before != grub_err_printed_errors)
+    grub_wait_after_message ();
+
+  errs_before = grub_err_printed_errors;
+
+  if (grub_errno == GRUB_ERR_NONE && grub_loader_is_loaded ())
+    /* Implicit execution of boot, only if something is loaded.  */
+    grub_command_execute ("boot", 0, 0);
+
+  if (errs_before != grub_err_printed_errors)
+    grub_wait_after_message ();
+
+  if (entry->submenu)
+    {
+      if (menu && menu->size)
+       {
+         grub_show_menu (menu, 1, auto_boot);
+         grub_normal_free_menu (menu);
+       }
+      grub_env_context_close ();
+    }
+  if (oldchosen)
+    grub_env_set ("chosen", oldchosen);
+  else
+    grub_env_unset ("chosen");
+  if (olddefault)
+    grub_env_set ("default", olddefault);
+  else
+    grub_env_unset ("default");
+  grub_env_unset ("timeout");
+}
+
+/* Execute ENTRY from the menu MENU, falling back to entries specified
+   in the environment variable "fallback" if it fails.  CALLBACK is a
+   pointer to a struct of function pointers which are used to allow the
+   caller provide feedback to the user.  */
+static void
+grub_menu_execute_with_fallback (grub_menu_t menu,
+                                grub_menu_entry_t entry,
+                                int autobooted,
+                                grub_menu_execute_callback_t callback,
+                                void *callback_data)
+{
+  int fallback_entry;
+
+  callback->notify_booting (entry, callback_data);
+
+  grub_menu_execute_entry (entry, 1);
+
+  /* Deal with fallback entries.  */
+  while ((fallback_entry = get_and_remove_first_entry_number ("fallback"))
+        >= 0)
+    {
+      grub_print_error ();
+      grub_errno = GRUB_ERR_NONE;
+
+      entry = grub_menu_get_entry (menu, fallback_entry);
+      callback->notify_fallback (entry, callback_data);
+      grub_menu_execute_entry (entry, 1);
+      /* If the function call to execute the entry returns at all, then this is
+        taken to indicate a boot failure.  For menu entries that do something
+        other than actually boot an operating system, this could assume
+        incorrectly that something failed.  */
+    }
+
+  if (!autobooted)
+    callback->notify_failure (callback_data);
+}
+
+static struct grub_menu_viewer *viewers;
+
+static void
+menu_set_chosen_entry (int entry)
+{
+  struct grub_menu_viewer *cur;
+  for (cur = viewers; cur; cur = cur->next)
+    cur->set_chosen_entry (entry, cur->data);
+}
+
+static void
+menu_print_timeout (int timeout)
+{
+  struct grub_menu_viewer *cur;
+  for (cur = viewers; cur; cur = cur->next)
+    cur->print_timeout (timeout, cur->data);
+}
+
+static void
+menu_fini (void)
+{
+  struct grub_menu_viewer *cur, *next;
+  for (cur = viewers; cur; cur = next)
+    {
+      next = cur->next;
+      cur->fini (cur->data);
+      grub_free (cur);
+    }
+  viewers = NULL;
+}
+
+static void
+menu_init (int entry, grub_menu_t menu, int nested)
+{
+  struct grub_term_output *term;
+  int gfxmenu = 0;
+
+  FOR_ACTIVE_TERM_OUTPUTS(term)
+    if (term->fullscreen)
+      {
+       if (grub_env_get ("theme"))
+         {
+           if (!grub_gfxmenu_try_hook)
+             {
+               grub_dl_load ("gfxmenu");
+               grub_print_error ();
+             }
+           if (grub_gfxmenu_try_hook)
+             {
+               grub_err_t err;
+               err = grub_gfxmenu_try_hook (entry, menu, nested);
+               if(!err)
+                 {
+                   gfxmenu = 1;
+                   break;
+                 }
+             }
+           else
+             grub_error (GRUB_ERR_BAD_MODULE,
+                         N_("module `%s' isn't loaded"),
+                         "gfxmenu");
+           grub_print_error ();
+           grub_wait_after_message ();
+         }
+       grub_errno = GRUB_ERR_NONE;
+       term->fullscreen ();
+       break;
+      }
+
+  FOR_ACTIVE_TERM_OUTPUTS(term)
+  {
+    grub_err_t err;
+
+    if (grub_strcmp (term->name, "gfxterm") == 0 && gfxmenu)
+      continue;
+
+    err = grub_menu_try_text (term, entry, menu, nested);
+    if(!err)
+      continue;
+    grub_print_error ();
+    grub_errno = GRUB_ERR_NONE;
+  }
+}
+
+static void
+clear_timeout (void)
+{
+  struct grub_menu_viewer *cur;
+  for (cur = viewers; cur; cur = cur->next)
+    cur->clear_timeout (cur->data);
+}
+
+void
+grub_menu_register_viewer (struct grub_menu_viewer *viewer)
+{
+  viewer->next = viewers;
+  viewers = viewer;
+}
+
+static int
+menuentry_eq (const char *id, const char *spec)
+{
+  const char *ptr1, *ptr2;
+  ptr1 = id;
+  ptr2 = spec;
+  while (1)
+    {
+      if (*ptr2 == '>' && ptr2[1] != '>' && *ptr1 == 0)
+       return 1;
+      if (*ptr2 == '>' && ptr2[1] != '>')
+       return 0;
+      if (*ptr2 == '>')
+       ptr2++;
+      if (*ptr1 != *ptr2)
+       return 0;
+      if (*ptr1 == 0)
+       return 1;
+      ptr1++;
+      ptr2++;
+    }
+}
+
+
+/* Get the entry number from the variable NAME.  */
+static int
+get_entry_number (grub_menu_t menu, const char *name)
+{
+  const char *val;
+  int entry;
+
+  val = grub_env_get (name);
+  if (! val)
+    return -1;
+
+  grub_error_push ();
+
+  entry = (int) grub_strtoul (val, 0, 0);
+
+  if (grub_errno == GRUB_ERR_BAD_NUMBER)
+    {
+      /* See if the variable matches the title of a menu entry.  */
+      grub_menu_entry_t e = menu->entry_list;
+      int i;
+
+      grub_errno = GRUB_ERR_NONE;
+
+      for (i = 0; e; i++)
+       {
+         if (menuentry_eq (e->title, val)
+             || menuentry_eq (e->id, val))
+           {
+             entry = i;
+             break;
+           }
+         e = e->next;
+       }
+
+      if (! e)
+       entry = -1;
+    }
+
+  if (grub_errno != GRUB_ERR_NONE)
+    {
+      grub_errno = GRUB_ERR_NONE;
+      entry = -1;
+    }
+
+  grub_error_pop ();
+
+  return entry;
+}
+
+/* Check whether a second has elapsed since the last tick.  If so, adjust
+   the timer and return 1; otherwise, return 0.  */
+static int
+has_second_elapsed (grub_uint64_t *saved_time)
+{
+  grub_uint64_t current_time;
+
+  current_time = grub_get_time_ms ();
+  if (current_time - *saved_time >= 1000)
+    {
+      *saved_time = current_time;
+      return 1;
+    }
+  else
+    return 0;
+}
+
+static void
+print_countdown (struct grub_term_coordinate *pos, int n)
+{
+  grub_term_restore_pos (pos);
+  /* NOTE: Do not remove the trailing space characters.
+     They are required to clear the line.  */
+  grub_printf ("%d    ", n);
+  grub_refresh ();
+}
+
+#define GRUB_MENU_PAGE_SIZE 10
+
+/* Show the menu and handle menu entry selection.  Returns the menu entry
+   index that should be executed or -1 if no entry should be executed (e.g.,
+   Esc pressed to exit a sub-menu or switching menu viewers).
+   If the return value is not -1, then *AUTO_BOOT is nonzero iff the menu
+   entry to be executed is a result of an automatic default selection because
+   of the timeout.  */
+static int
+run_menu (grub_menu_t menu, int nested, int *auto_boot)
+{
+  grub_uint64_t saved_time;
+  int default_entry, current_entry;
+  int timeout;
+  enum timeout_style timeout_style;
+
+  default_entry = get_entry_number (menu, "default");
+
+  /* If DEFAULT_ENTRY is not within the menu entries, fall back to
+     the first entry.  */
+  if (default_entry < 0 || default_entry >= menu->size)
+    default_entry = 0;
+
+  timeout = grub_menu_get_timeout ();
+  if (timeout < 0)
+    /* If there is no timeout, the "countdown" and "hidden" styles result in
+       the system doing nothing and providing no or very little indication
+       why.  Technically this is what the user asked for, but it's not very
+       useful and likely to be a source of confusion, so we disallow this.  */
+    grub_env_unset ("timeout_style");
+
+  timeout_style = get_timeout_style ();
+
+  if (timeout_style == TIMEOUT_STYLE_COUNTDOWN
+      || timeout_style == TIMEOUT_STYLE_HIDDEN)
+    {
+      static struct grub_term_coordinate *pos;
+      int entry = -1;
+
+      if (timeout_style == TIMEOUT_STYLE_COUNTDOWN && timeout)
+       {
+         pos = grub_term_save_pos ();
+         print_countdown (pos, timeout);
+       }
+
+      /* Enter interruptible sleep until Escape or a menu hotkey is pressed,
+         or the timeout expires.  */
+      saved_time = grub_get_time_ms ();
+      while (1)
+       {
+         int key;
+
+         key = grub_getkey_noblock ();
+         if (key != GRUB_TERM_NO_KEY)
+           {
+             entry = get_entry_index_by_hotkey (menu, key);
+             if (entry >= 0)
+               break;
+           }
+         if (key == GRUB_TERM_ESC)
+           {
+             timeout = -1;
+             break;
+           }
+
+         if (timeout > 0 && has_second_elapsed (&saved_time))
+           {
+             timeout--;
+             if (timeout_style == TIMEOUT_STYLE_COUNTDOWN)
+               print_countdown (pos, timeout);
+           }
+
+         if (timeout == 0)
+           /* We will fall through to auto-booting the default entry.  */
+           break;
+       }
+
+      grub_env_unset ("timeout");
+      grub_env_unset ("timeout_style");
+      if (entry >= 0)
+       {
+         *auto_boot = 0;
+         return entry;
+       }
+    }
+
+  /* If timeout is 0, drawing is pointless (and ugly).  */
+  if (timeout == 0)
+    {
+      *auto_boot = 1;
+      return default_entry;
+    }
+
+  current_entry = default_entry;
+
+ refresh:
+  menu_init (current_entry, menu, nested);
+
+  /* Initialize the time.  */
+  saved_time = grub_get_time_ms ();
+
+  timeout = grub_menu_get_timeout ();
+
+  if (timeout > 0)
+    menu_print_timeout (timeout);
+  else
+    clear_timeout ();
+
+  while (1)
+    {
+      int c;
+      timeout = grub_menu_get_timeout ();
+
+      if (grub_normal_exit_level)
+       return -1;
+
+      if (timeout > 0 && has_second_elapsed (&saved_time))
+       {
+         timeout--;
+         grub_menu_set_timeout (timeout);
+         menu_print_timeout (timeout);
+       }
+
+      if (timeout == 0)
+       {
+         grub_env_unset ("timeout");
+          *auto_boot = 1;
+         menu_fini ();
+         return default_entry;
+       }
+
+      c = grub_getkey_noblock ();
+
+      /* Negative values are returned on error. */
+      if ((c != GRUB_TERM_NO_KEY) && (c > 0))
+       {
+         if (timeout >= 0)
+           {
+             grub_env_unset ("timeout");
+             grub_env_unset ("fallback");
+             clear_timeout ();
+           }
+
+         switch (c)
+           {
+           case GRUB_TERM_KEY_HOME:
+           case GRUB_TERM_CTRL | 'a':
+             current_entry = 0;
+             menu_set_chosen_entry (current_entry);
+             break;
+
+           case GRUB_TERM_KEY_END:
+           case GRUB_TERM_CTRL | 'e':
+             current_entry = menu->size - 1;
+             menu_set_chosen_entry (current_entry);
+             break;
+
+           case GRUB_TERM_KEY_UP:
+           case GRUB_TERM_CTRL | 'p':
+           case '^':
+             if (current_entry > 0)
+               current_entry--;
+             menu_set_chosen_entry (current_entry);
+             break;
+
+           case GRUB_TERM_CTRL | 'n':
+           case GRUB_TERM_KEY_DOWN:
+           case 'v':
+             if (current_entry < menu->size - 1)
+               current_entry++;
+             menu_set_chosen_entry (current_entry);
+             break;
+
+           case GRUB_TERM_CTRL | 'g':
+           case GRUB_TERM_KEY_PPAGE:
+             if (current_entry < GRUB_MENU_PAGE_SIZE)
+               current_entry = 0;
+             else
+               current_entry -= GRUB_MENU_PAGE_SIZE;
+             menu_set_chosen_entry (current_entry);
+             break;
+
+           case GRUB_TERM_CTRL | 'c':
+           case GRUB_TERM_KEY_NPAGE:
+             if (current_entry + GRUB_MENU_PAGE_SIZE < menu->size)
+               current_entry += GRUB_MENU_PAGE_SIZE;
+             else
+               current_entry = menu->size - 1;
+             menu_set_chosen_entry (current_entry);
+             break;
+
+           case '\n':
+           case '\r':
+       //    case GRUB_TERM_KEY_RIGHT:
+           case GRUB_TERM_CTRL | 'f':
+             menu_fini ();
+              *auto_boot = 0;
+             return current_entry;
+
+           case GRUB_TERM_ESC:
+             if (nested)
+               {
+                 menu_fini ();
+                 return -1;
+               }
+             break;
+
+           case 'c':
+             menu_fini ();
+             grub_cmdline_run (1, 0);
+             goto refresh;
+
+           case 'e':
+             menu_fini ();
+               {
+                 grub_menu_entry_t e = grub_menu_get_entry (menu, current_entry);
+                 if (e)
+                   grub_menu_entry_run (e);
+               }
+             goto refresh;
+
+           default:
+             {
+               int entry;
+
+               entry = get_entry_index_by_hotkey (menu, c);
+               if (entry >= 0)
+                 {
+                   menu_fini ();
+                   *auto_boot = 0;
+                   return entry;
+                 }
+             }
+             break;
+           }
+       }
+    }
+
+  /* Never reach here.  */
+}
+
+/* Callback invoked immediately before a menu entry is executed.  */
+static void
+notify_booting (grub_menu_entry_t entry,
+               void *userdata __attribute__((unused)))
+{
+  grub_printf ("  ");
+  grub_printf_ (N_("Booting `%s'"), entry->title);
+  grub_printf ("\n\n");
+}
+
+/* Callback invoked when a default menu entry executed because of a timeout
+   has failed and an attempt will be made to execute the next fallback
+   entry, ENTRY.  */
+static void
+notify_fallback (grub_menu_entry_t entry,
+                void *userdata __attribute__((unused)))
+{
+  grub_printf ("\n   ");
+  grub_printf_ (N_("Falling back to `%s'"), entry->title);
+  grub_printf ("\n\n");
+  grub_millisleep (DEFAULT_ENTRY_ERROR_DELAY_MS);
+}
+
+/* Callback invoked when a menu entry has failed and there is no remaining
+   fallback entry to attempt.  */
+static void
+notify_execution_failure (void *userdata __attribute__((unused)))
+{
+  if (grub_errno != GRUB_ERR_NONE)
+    {
+      grub_print_error ();
+      grub_errno = GRUB_ERR_NONE;
+    }
+  grub_printf ("\n  ");
+  grub_printf_ (N_("Failed to boot both default and fallback entries.\n"));
+  grub_wait_after_message ();
+}
+
+/* Callbacks used by the text menu to provide user feedback when menu entries
+   are executed.  */
+static struct grub_menu_execute_callback execution_callback =
+{
+  .notify_booting = notify_booting,
+  .notify_fallback = notify_fallback,
+  .notify_failure = notify_execution_failure
+};
+
+static grub_err_t
+show_menu (grub_menu_t menu, int nested, int autobooted)
+{
+  while (1)
+    {
+      int boot_entry;
+      grub_menu_entry_t e;
+      int auto_boot;
+
+      boot_entry = run_menu (menu, nested, &auto_boot);
+      if (boot_entry < 0)
+       break;
+
+      e = grub_menu_get_entry (menu, boot_entry);
+      if (! e)
+       continue; /* Menu is empty.  */
+
+      grub_cls ();
+
+      if (auto_boot)
+       grub_menu_execute_with_fallback (menu, e, autobooted,
+                                        &execution_callback, 0);
+      else
+       grub_menu_execute_entry (e, 0);
+      if (autobooted)
+       break;
+    }
+
+  return GRUB_ERR_NONE;
+}
+
+grub_err_t
+grub_show_menu (grub_menu_t menu, int nested, int autoboot)
+{
+  grub_err_t err1, err2;
+
+  while (1)
+    {
+      err1 = show_menu (menu, nested, autoboot);
+      autoboot = 0;
+      grub_print_error ();
+
+      if (grub_normal_exit_level)
+       break;
+
+      err2 = grub_auth_check_authentication (NULL);
+      if (err2)
+       {
+         grub_print_error ();
+         grub_errno = GRUB_ERR_NONE;
+         continue;
+       }
+
+      break;
+    }
+
+  return err1;
+}
diff --git a/GRUB2/grub-2.04/grub-core/ventoy/huffman.c b/GRUB2/grub-2.04/grub-core/ventoy/huffman.c
new file mode 100644 (file)
index 0000000..1680056
--- /dev/null
@@ -0,0 +1,207 @@
+/*
+ * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * @file
+ *
+ * Huffman alphabets
+ *
+ */
+
+#include "wimboot.h"
+#include "huffman.h"
+
+/**
+ * Transcribe binary value (for debugging)
+ *
+ * @v value            Value
+ * @v bits             Length of value (in bits)
+ * @ret string         Transcribed value
+ */
+const char * huffman_bin ( unsigned long value, unsigned int bits ) {
+       static char buf[ ( 8 * sizeof ( value ) ) + 1 /* NUL */ ];
+       char *out = buf;
+
+       /* Sanity check */
+       assert ( bits < sizeof ( buf ) );
+
+       /* Transcribe value */
+       while ( bits-- )
+               *(out++) = ( ( value & ( 1 << bits ) ) ? '1' : '0' );
+       *out = '\0';
+
+       return buf;
+}
+
+/**
+ * Dump Huffman alphabet (for debugging)
+ *
+ * @v alphabet         Huffman alphabet
+ */
+static void __attribute__ (( unused ))
+huffman_dump_alphabet ( struct huffman_alphabet *alphabet ) {
+       struct huffman_symbols *sym;
+       unsigned int bits;
+       unsigned int huf;
+       unsigned int i;
+
+    (void)huf;
+
+       /* Dump symbol table for each utilised length */
+       for ( bits = 1 ; bits <= ( sizeof ( alphabet->huf ) /
+                                  sizeof ( alphabet->huf[0] ) ) ; bits++ ) {
+               sym = &alphabet->huf[ bits - 1 ];
+               if ( sym->freq == 0 )
+                       continue;
+               huf = ( sym->start >> sym->shift );
+               DBG ( "Huffman length %d start \"%s\" freq %d:", bits,
+                     huffman_bin ( huf, sym->bits ), sym->freq );
+               for ( i = 0 ; i < sym->freq ; i++ ) {
+                       DBG ( " %03x", sym->raw[ huf + i ] );
+               }
+               DBG ( "\n" );
+       }
+
+       /* Dump quick lookup table */
+       DBG ( "Huffman quick lookup:" );
+       for ( i = 0 ; i < ( sizeof ( alphabet->lookup ) /
+                           sizeof ( alphabet->lookup[0] ) ) ; i++ ) {
+               DBG ( " %d", ( alphabet->lookup[i] + 1 ) );
+       }
+       DBG ( "\n" );
+}
+
+/**
+ * Construct Huffman alphabet
+ *
+ * @v alphabet         Huffman alphabet
+ * @v lengths          Symbol length table
+ * @v count            Number of symbols
+ * @ret rc             Return status code
+ */
+int huffman_alphabet ( struct huffman_alphabet *alphabet,
+                      uint8_t *lengths, unsigned int count ) {
+       struct huffman_symbols *sym;
+       unsigned int huf;
+       unsigned int cum_freq;
+       unsigned int bits;
+       unsigned int raw;
+       unsigned int adjustment;
+       unsigned int prefix;
+       int empty;
+       int complete;
+
+       /* Clear symbol table */
+       memset ( alphabet->huf, 0, sizeof ( alphabet->huf ) );
+
+       /* Count number of symbols with each Huffman-coded length */
+       empty = 1;
+       for ( raw = 0 ; raw < count ; raw++ ) {
+               bits = lengths[raw];
+               if ( bits ) {
+                       alphabet->huf[ bits - 1 ].freq++;
+                       empty = 0;
+               }
+       }
+
+       /* In the degenerate case of having no symbols (i.e. an unused
+        * alphabet), generate a trivial alphabet with exactly two
+        * single-bit codes.  This allows callers to avoid having to
+        * check for this special case.
+        */
+       if ( empty )
+               alphabet->huf[0].freq = 2;
+
+       /* Populate Huffman-coded symbol table */
+       huf = 0;
+       cum_freq = 0;
+       for ( bits = 1 ; bits <= ( sizeof ( alphabet->huf ) /
+                                  sizeof ( alphabet->huf[0] ) ) ; bits++ ) {
+               sym = &alphabet->huf[ bits - 1 ];
+               sym->bits = bits;
+               sym->shift = ( HUFFMAN_BITS - bits );
+               sym->start = ( huf << sym->shift );
+               sym->raw = &alphabet->raw[cum_freq];
+               huf += sym->freq;
+               if ( huf > ( 1U << bits ) ) {
+                       DBG ( "Huffman alphabet has too many symbols with "
+                             "lengths <=%d\n", bits );
+                       return -1;
+               }
+               huf <<= 1;
+               cum_freq += sym->freq;
+       }
+       complete = ( huf == ( 1U << bits ) );
+
+       /* Populate raw symbol table */
+       for ( raw = 0 ; raw < count ; raw++ ) {
+               bits = lengths[raw];
+               if ( bits ) {
+                       sym = &alphabet->huf[ bits - 1 ];
+                       *(sym->raw++) = raw;
+               }
+       }
+
+       /* Adjust Huffman-coded symbol table raw pointers and populate
+        * quick lookup table.
+        */
+       for ( bits = 1 ; bits <= ( sizeof ( alphabet->huf ) /
+                                  sizeof ( alphabet->huf[0] ) ) ; bits++ ) {
+               sym = &alphabet->huf[ bits - 1 ];
+
+               /* Adjust raw pointer */
+               sym->raw -= sym->freq; /* Reset to first symbol */
+               adjustment = ( sym->start >> sym->shift );
+               sym->raw -= adjustment; /* Adjust for quick indexing */
+
+               /* Populate quick lookup table */
+               for ( prefix = ( sym->start >> HUFFMAN_QL_SHIFT ) ;
+                     prefix < ( 1 << HUFFMAN_QL_BITS ) ; prefix++ ) {
+                       alphabet->lookup[prefix] = ( bits - 1 );
+               }
+       }
+
+       /* Check that there are no invalid codes */
+       if ( ! complete ) {
+               DBG ( "Huffman alphabet is incomplete\n" );
+               return -1;
+       }
+
+       return 0;
+}
+
+/**
+ * Get Huffman symbol set
+ *
+ * @v alphabet         Huffman alphabet
+ * @v huf              Raw input value (normalised to HUFFMAN_BITS bits)
+ * @ret sym            Huffman symbol set
+ */
+struct huffman_symbols * huffman_sym ( struct huffman_alphabet *alphabet,
+                                      unsigned int huf ) {
+       struct huffman_symbols *sym;
+       unsigned int lookup_index;
+
+       /* Find symbol set for this length */
+       lookup_index = ( huf >> HUFFMAN_QL_SHIFT );
+       sym = &alphabet->huf[ alphabet->lookup[ lookup_index ] ];
+       while ( huf < sym->start )
+               sym--;
+       return sym;
+}
diff --git a/GRUB2/grub-2.04/grub-core/ventoy/huffman.h b/GRUB2/grub-2.04/grub-core/ventoy/huffman.h
new file mode 100644 (file)
index 0000000..95bbae8
--- /dev/null
@@ -0,0 +1,108 @@
+#ifndef _HUFFMAN_H
+#define _HUFFMAN_H
+
+/*
+ * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * @file
+ *
+ * Huffman alphabets
+ *
+ */
+
+/** Maximum length of a Huffman symbol (in bits) */
+#define HUFFMAN_BITS 16
+
+/** Raw huffman symbol */
+typedef uint16_t huffman_raw_symbol_t;
+
+/** Quick lookup length for a Huffman symbol (in bits)
+ *
+ * This is a policy decision.
+ */
+#define HUFFMAN_QL_BITS 7
+
+/** Quick lookup shift */
+#define HUFFMAN_QL_SHIFT ( HUFFMAN_BITS - HUFFMAN_QL_BITS )
+
+/** A Huffman-coded set of symbols of a given length */
+struct huffman_symbols {
+       /** Length of Huffman-coded symbols (in bits) */
+       uint8_t bits;
+       /** Shift to normalise symbols of this length to HUFFMAN_BITS bits */
+       uint8_t shift;
+       /** Number of Huffman-coded symbols having this length */
+       uint16_t freq;
+       /** First symbol of this length (normalised to HUFFMAN_BITS bits)
+        *
+        * Stored as a 32-bit value to allow the value
+        * (1<<HUFFMAN_BITS ) to be used for empty sets of symbols
+        * longer than the maximum utilised length.
+        */
+       uint32_t start;
+       /** Raw symbols having this length */
+       huffman_raw_symbol_t *raw;
+};
+
+/** A Huffman-coded alphabet */
+struct huffman_alphabet {
+       /** Huffman-coded symbol set for each length */
+       struct huffman_symbols huf[HUFFMAN_BITS];
+       /** Quick lookup table */
+       uint8_t lookup[ 1 << HUFFMAN_QL_BITS ];
+       /** Raw symbols
+        *
+        * Ordered by Huffman-coded symbol length, then by symbol
+        * value.  This field has a variable length.
+        */
+       huffman_raw_symbol_t raw[0];
+};
+
+/**
+ * Get Huffman symbol length
+ *
+ * @v sym              Huffman symbol set
+ * @ret len            Length (in bits)
+ */
+static inline __attribute__ (( always_inline )) unsigned int
+huffman_len ( struct huffman_symbols *sym ) {
+
+       return sym->bits;
+}
+
+/**
+ * Get Huffman symbol value
+ *
+ * @v sym              Huffman symbol set
+ * @v huf              Raw input value (normalised to HUFFMAN_BITS bits)
+ * @ret raw            Raw symbol value
+ */
+static inline __attribute__ (( always_inline )) huffman_raw_symbol_t
+huffman_raw ( struct huffman_symbols *sym, unsigned int huf ) {
+
+       return sym->raw[ huf >> sym->shift ];
+}
+
+extern int huffman_alphabet ( struct huffman_alphabet *alphabet,
+                             uint8_t *lengths, unsigned int count );
+extern struct huffman_symbols *
+huffman_sym ( struct huffman_alphabet *alphabet, unsigned int huf );
+
+#endif /* _HUFFMAN_H */
diff --git a/GRUB2/grub-2.04/grub-core/ventoy/lzx.c b/GRUB2/grub-2.04/grub-core/ventoy/lzx.c
new file mode 100644 (file)
index 0000000..56dd7dd
--- /dev/null
@@ -0,0 +1,666 @@
+/*
+ * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * @file
+ *
+ * LZX decompression
+ *
+ * This algorithm is derived jointly from the document "[MS-PATCH]:
+ * LZX DELTA Compression and Decompression", available from
+ *
+ *     http://msdn.microsoft.com/en-us/library/cc483133.aspx
+ *
+ * and from the file lzx-decompress.c in the wimlib source code.
+ *
+ */
+
+#include "wimboot.h"
+#include "huffman.h"
+#include "lzx.h"
+
+/** Base positions, indexed by position slot */
+static unsigned int lzx_position_base[LZX_POSITION_SLOTS];
+
+/**
+ * Attempt to accumulate bits from LZX bitstream
+ *
+ * @v lzx              Decompressor
+ * @v bits             Number of bits to accumulate
+ * @v norm_value       Accumulated value (normalised to 16 bits)
+ *
+ * Note that there may not be sufficient accumulated bits in the
+ * bitstream; callers must check that sufficient bits are available
+ * before using the value.
+ */
+static int lzx_accumulate ( struct lzx *lzx, unsigned int bits ) {
+       const uint16_t *src16;
+
+       /* Accumulate more bits if required */
+       if ( ( lzx->bits < bits ) &&
+            ( lzx->input.offset < lzx->input.len ) ) {
+               src16 = (const uint16_t *)( ( char * ) lzx->input.data + lzx->input.offset );
+               lzx->input.offset += sizeof ( *src16 );
+               lzx->accumulator |= ( *src16 << ( 16 - lzx->bits ) );
+               lzx->bits += 16;
+       }
+
+       return ( lzx->accumulator >> 16 );
+}
+
+/**
+ * Consume accumulated bits from LZX bitstream
+ *
+ * @v lzx              Decompressor
+ * @v bits             Number of bits to consume
+ * @ret rc             Return status code
+ */
+static int lzx_consume ( struct lzx *lzx, unsigned int bits ) {
+
+       /* Fail if insufficient bits are available */
+       if ( lzx->bits < bits ) {
+               DBG ( "LZX input overrun in %#zx/%#zx out %#zx)\n",
+                     lzx->input.offset, lzx->input.len, lzx->output.offset );
+               return -1;
+       }
+
+       /* Consume bits */
+       lzx->accumulator <<= bits;
+       lzx->bits -= bits;
+
+       return 0;
+}
+
+/**
+ * Get bits from LZX bitstream
+ *
+ * @v lzx              Decompressor
+ * @v bits             Number of bits to fetch
+ * @ret value          Value, or negative error
+ */
+static int lzx_getbits ( struct lzx *lzx, unsigned int bits ) {
+       int norm_value;
+       int rc;
+
+       /* Accumulate more bits if required */
+       norm_value = lzx_accumulate ( lzx, bits );
+
+       /* Consume bits */
+       if ( ( rc = lzx_consume ( lzx, bits ) ) != 0 )
+               return rc;
+
+       return ( norm_value >> ( 16 - bits ) );
+}
+
+/**
+ * Align LZX bitstream for byte access
+ *
+ * @v lzx              Decompressor
+ * @v bits             Minimum number of padding bits
+ * @ret rc             Return status code
+ */
+static int lzx_align ( struct lzx *lzx, unsigned int bits ) {
+       int pad;
+
+       /* Get padding bits */
+       pad = lzx_getbits ( lzx, bits );
+       if ( pad < 0 )
+               return pad;
+
+       /* Consume all accumulated bits */
+       lzx_consume ( lzx, lzx->bits );
+
+       return 0;
+}
+
+/**
+ * Get bytes from LZX bitstream
+ *
+ * @v lzx              Decompressor
+ * @v data             Data buffer, or NULL
+ * @v len              Length of data buffer
+ * @ret rc             Return status code
+ */
+static int lzx_getbytes ( struct lzx *lzx, void *data, size_t len ) {
+
+       /* Sanity check */
+       if ( ( lzx->input.offset + len ) > lzx->input.len ) {
+               DBG ( "LZX input overrun in %#zx/%#zx out %#zx)\n",
+                     lzx->input.offset, lzx->input.len, lzx->output.offset );
+               return -1;
+       }
+
+       /* Copy data */
+       if ( data )
+               memcpy ( data, ( lzx->input.data + lzx->input.offset ), len );
+       lzx->input.offset += len;
+
+       return 0;
+}
+
+/**
+ * Decode LZX Huffman-coded symbol
+ *
+ * @v lzx              Decompressor
+ * @v alphabet         Huffman alphabet
+ * @ret raw            Raw symbol, or negative error
+ */
+static int lzx_decode ( struct lzx *lzx, struct huffman_alphabet *alphabet ) {
+       struct huffman_symbols *sym;
+       int huf;
+       int rc;
+
+       /* Accumulate sufficient bits */
+       huf = lzx_accumulate ( lzx, HUFFMAN_BITS );
+       if ( huf < 0 )
+               return huf;
+
+       /* Decode symbol */
+       sym = huffman_sym ( alphabet, huf );
+
+       /* Consume bits */
+       if ( ( rc = lzx_consume ( lzx, huffman_len ( sym ) ) ) != 0 )
+               return rc;
+
+       return huffman_raw ( sym, huf );
+}
+
+/**
+ * Generate Huffman alphabet from raw length table
+ *
+ * @v lzx              Decompressor
+ * @v count            Number of symbols
+ * @v bits             Length of each length (in bits)
+ * @v lengths          Lengths table to fill in
+ * @v alphabet         Huffman alphabet to fill in
+ * @ret rc             Return status code
+ */
+static int lzx_raw_alphabet ( struct lzx *lzx, unsigned int count,
+                             unsigned int bits, uint8_t *lengths,
+                             struct huffman_alphabet *alphabet ) {
+       unsigned int i;
+       int len;
+       int rc;
+
+       /* Read lengths */
+       for ( i = 0 ; i < count ; i++ ) {
+               len = lzx_getbits ( lzx, bits );
+               if ( len < 0 )
+                       return len;
+               lengths[i] = len;
+       }
+
+       /* Generate Huffman alphabet */
+       if ( ( rc = huffman_alphabet ( alphabet, lengths, count ) ) != 0 )
+               return rc;
+
+       return 0;
+}
+
+/**
+ * Generate pretree
+ *
+ * @v lzx              Decompressor
+ * @v count            Number of symbols
+ * @v lengths          Lengths table to fill in
+ * @ret rc             Return status code
+ */
+static int lzx_pretree ( struct lzx *lzx, unsigned int count,
+                        uint8_t *lengths ) {
+       unsigned int i;
+       unsigned int length;
+       int dup = 0;
+       int code;
+       int rc;
+
+       /* Generate pretree alphabet */
+       if ( ( rc =  lzx_raw_alphabet ( lzx, LZX_PRETREE_CODES,
+                                       LZX_PRETREE_BITS, lzx->pretree_lengths,
+                                       &lzx->pretree ) ) != 0 )
+               return rc;
+
+       /* Read lengths */
+       for ( i = 0 ; i < count ; i++ ) {
+
+               if ( dup ) {
+
+                       /* Duplicate previous length */
+                       lengths[i] = lengths[ i - 1 ];
+                       dup--;
+
+               } else {
+
+                       /* Get next code */
+                       code = lzx_decode ( lzx, &lzx->pretree );
+                       if ( code < 0 )
+                               return code;
+
+                       /* Interpret code */
+                       if ( code <= 16 ) {
+                               length = ( ( lengths[i] - code + 17 ) % 17 );
+                       } else if ( code == 17 ) {
+                               length = 0;
+                               dup = lzx_getbits ( lzx, 4 );
+                               if ( dup < 0 )
+                                       return dup;
+                               dup += 3;
+                       } else if ( code == 18 ) {
+                               length = 0;
+                               dup = lzx_getbits ( lzx, 5 );
+                               if ( dup < 0 )
+                                       return dup;
+                               dup += 19;
+                       } else if ( code == 19 ) {
+                               length = 0;
+                               dup = lzx_getbits ( lzx, 1 );
+                               if ( dup < 0 )
+                                       return dup;
+                               dup += 3;
+                               code = lzx_decode ( lzx, &lzx->pretree );
+                               if ( code < 0 )
+                                       return code;
+                               length = ( ( lengths[i] - code + 17 ) % 17 );
+                       } else {
+                               DBG ( "Unrecognised pretree code %d\n", code );
+                               return -1;
+                       }
+                       lengths[i] = length;
+               }
+       }
+
+       /* Sanity check */
+       if ( dup ) {
+               DBG ( "Pretree duplicate overrun\n" );
+               return -1;
+       }
+
+       return 0;
+}
+
+/**
+ * Generate aligned offset Huffman alphabet
+ *
+ * @v lzx              Decompressor
+ * @ret rc             Return status code
+ */
+static int lzx_alignoffset_alphabet ( struct lzx *lzx ) {
+       int rc;
+
+       /* Generate aligned offset alphabet */
+       if ( ( rc = lzx_raw_alphabet ( lzx, LZX_ALIGNOFFSET_CODES,
+                                      LZX_ALIGNOFFSET_BITS,
+                                      lzx->alignoffset_lengths,
+                                      &lzx->alignoffset ) ) != 0 )
+               return rc;
+
+       return 0;
+}
+
+/**
+ * Generate main Huffman alphabet
+ *
+ * @v lzx              Decompressor
+ * @ret rc             Return status code
+ */
+static int lzx_main_alphabet ( struct lzx *lzx ) {
+       int rc;
+
+       /* Generate literal symbols pretree */
+       if ( ( rc = lzx_pretree ( lzx, LZX_MAIN_LIT_CODES,
+                                 lzx->main_lengths.literals ) ) != 0 ) {
+               DBG ( "Could not construct main literal pretree\n" );
+               return rc;
+       }
+
+       /* Generate remaining symbols pretree */
+       if ( ( rc = lzx_pretree ( lzx, ( LZX_MAIN_CODES - LZX_MAIN_LIT_CODES ),
+                                 lzx->main_lengths.remainder ) ) != 0 ) {
+               DBG ( "Could not construct main remainder pretree\n" );
+               return rc;
+       }
+
+       /* Generate Huffman alphabet */
+       if ( ( rc = huffman_alphabet ( &lzx->main, lzx->main_lengths.literals,
+                                      LZX_MAIN_CODES ) ) != 0 ) {
+               DBG ( "Could not generate main alphabet\n" );
+               return rc;
+       }
+
+       return 0;
+}
+
+/**
+ * Generate length Huffman alphabet
+ *
+ * @v lzx              Decompressor
+ * @ret rc             Return status code
+ */
+static int lzx_length_alphabet ( struct lzx *lzx ) {
+       int rc;
+
+       /* Generate pretree */
+       if ( ( rc = lzx_pretree ( lzx, LZX_LENGTH_CODES,
+                                 lzx->length_lengths ) ) != 0 ) {
+               DBG ( "Could not generate length pretree\n" );
+               return rc;
+       }
+
+       /* Generate Huffman alphabet */
+       if ( ( rc = huffman_alphabet ( &lzx->length, lzx->length_lengths,
+                                      LZX_LENGTH_CODES ) ) != 0 ) {
+               DBG ( "Could not generate length alphabet\n" );
+               return rc;
+       }
+
+       return 0;
+}
+
+/**
+ * Process LZX block header
+ *
+ * @v lzx              Decompressor
+ * @ret rc             Return status code
+ */
+static int lzx_block_header ( struct lzx *lzx ) {
+       size_t block_len;
+       int block_type;
+       int default_len;
+       int len_high;
+       int len_low;
+       int rc;
+
+       /* Get block type */
+       block_type = lzx_getbits ( lzx, LZX_BLOCK_TYPE_BITS );
+       if ( block_type < 0 )
+               return block_type;
+       lzx->block_type = block_type;
+
+       /* Check block length */
+       default_len = lzx_getbits ( lzx, 1 );
+       if ( default_len < 0 )
+               return default_len;
+       if ( default_len ) {
+               block_len = LZX_DEFAULT_BLOCK_LEN;
+       } else {
+               len_high = lzx_getbits ( lzx, 8 );
+               if ( len_high < 0 )
+                       return len_high;
+               len_low = lzx_getbits ( lzx, 8 );
+               if ( len_low < 0 )
+                       return len_low;
+               block_len = ( ( len_high << 8 ) | len_low );
+       }
+       lzx->output.threshold = ( lzx->output.offset + block_len );
+
+       /* Handle block type */
+       switch ( block_type ) {
+       case LZX_BLOCK_ALIGNOFFSET :
+               /* Generated aligned offset alphabet */
+               if ( ( rc = lzx_alignoffset_alphabet ( lzx ) ) != 0 )
+                       return rc;
+               /* Fall through */
+       case LZX_BLOCK_VERBATIM :
+               /* Generate main alphabet */
+               if ( ( rc = lzx_main_alphabet ( lzx ) ) != 0 )
+                       return rc;
+               /* Generate lengths alphabet */
+               if ( ( rc = lzx_length_alphabet ( lzx ) ) != 0 )
+                       return rc;
+               break;
+       case LZX_BLOCK_UNCOMPRESSED :
+               /* Align input stream */
+               if ( ( rc = lzx_align ( lzx, 1 ) ) != 0 )
+                       return rc;
+               /* Read new repeated offsets */
+               if ( ( rc = lzx_getbytes ( lzx, &lzx->repeated_offset,
+                                          sizeof ( lzx->repeated_offset )))!=0)
+                       return rc;
+               break;
+       default:
+               DBG ( "Unrecognised block type %d\n", block_type );
+               return -1;
+       }
+
+       return 0;
+}
+
+/**
+ * Process uncompressed data
+ *
+ * @v lzx              Decompressor
+ * @ret rc             Return status code
+ */
+static int lzx_uncompressed ( struct lzx *lzx ) {
+       void *data;
+       size_t len;
+       int rc;
+
+       /* Copy bytes */
+       data = ( lzx->output.data ?
+                ( lzx->output.data + lzx->output.offset ) : NULL );
+       len = ( lzx->output.threshold - lzx->output.offset );
+       if ( ( rc = lzx_getbytes ( lzx, data, len ) ) != 0 )
+               return rc;
+
+       /* Align input stream */
+       if ( len % 2 )
+               lzx->input.offset++;
+
+       return 0;
+}
+
+/**
+ * Process an LZX token
+ *
+ * @v lzx              Decompressor
+ * @ret rc             Return status code
+ *
+ * Variable names are chosen to match the LZX specification
+ * pseudo-code.
+ */
+static int lzx_token ( struct lzx *lzx ) {
+       unsigned int length_header;
+       unsigned int position_slot;
+       unsigned int offset_bits;
+       unsigned int i;
+       size_t match_offset;
+       size_t match_length;
+       int verbatim_bits;
+       int aligned_bits;
+       int maindata;
+       int length;
+       uint8_t *copy;
+
+       /* Get maindata symelse*/
+       maindata = lzx_decode ( lzx, &lzx->main );
+       if ( maindata < 0 )
+               return maindata;
+
+       /* Check for literals */
+       if ( maindata < LZX_MAIN_LIT_CODES ) {
+               if ( lzx->output.data )
+                       lzx->output.data[lzx->output.offset] = maindata;
+               lzx->output.offset++;
+               return 0;
+       }
+       maindata -= LZX_MAIN_LIT_CODES;
+
+       /* Calculate the match length */
+       length_header = ( maindata & 7 );
+       if ( length_header == 7 ) {
+               length = lzx_decode ( lzx, &lzx->length );
+               if ( length < 0 )
+                       return length;
+       } else {
+               length = 0;
+       }
+       match_length = ( length_header + 2 + length );
+
+       /* Calculate the position slot */
+       position_slot = ( maindata >> 3 );
+       if ( position_slot < LZX_REPEATED_OFFSETS ) {
+
+               /* Repeated offset */
+               match_offset = lzx->repeated_offset[position_slot];
+               lzx->repeated_offset[position_slot] = lzx->repeated_offset[0];
+               lzx->repeated_offset[0] = match_offset;
+
+       } else {
+
+               /* Non-repeated offset */
+               offset_bits = lzx_footer_bits ( position_slot );
+               if ( ( lzx->block_type == LZX_BLOCK_ALIGNOFFSET ) &&
+                    ( offset_bits >= 3 ) ) {
+                       verbatim_bits = lzx_getbits ( lzx, ( offset_bits - 3 ));
+                       if ( verbatim_bits < 0 )
+                               return verbatim_bits;
+                       verbatim_bits <<= 3;
+                       aligned_bits = lzx_decode ( lzx, &lzx->alignoffset );
+                       if ( aligned_bits < 0 )
+                               return aligned_bits;
+               } else {
+                       verbatim_bits = lzx_getbits ( lzx, offset_bits );
+                       if ( verbatim_bits < 0 )
+                               return verbatim_bits;
+                       aligned_bits = 0;
+               }
+               match_offset = ( lzx_position_base[position_slot] +
+                                verbatim_bits + aligned_bits - 2 );
+
+               /* Update repeated offset list */
+               for ( i = ( LZX_REPEATED_OFFSETS - 1 ) ; i > 0 ; i-- )
+                       lzx->repeated_offset[i] = lzx->repeated_offset[ i - 1 ];
+               lzx->repeated_offset[0] = match_offset;
+       }
+
+       /* Copy data */
+       if ( match_offset > lzx->output.offset ) {
+               DBG ( "LZX match underrun out 0x%x offset 0x%x len 0x%x\n",
+                     lzx->output.offset, match_offset, match_length );
+               return -1;
+       }
+       if ( lzx->output.data ) {
+               copy = &lzx->output.data[lzx->output.offset];
+               for ( i = 0 ; i < match_length ; i++ )
+                       copy[i] = copy[ i - match_offset ];
+       }
+       lzx->output.offset += match_length;
+
+       return 0;
+}
+
+/**
+ * Translate E8 jump addresses
+ *
+ * @v lzx              Decompressor
+ */
+static void lzx_translate_jumps ( struct lzx *lzx ) {
+       size_t offset;
+       int32_t *target;
+
+       /* Sanity check */
+       if ( lzx->output.offset < 10 )
+               return;
+
+       /* Scan for jump instructions */
+       for ( offset = 0 ; offset < ( lzx->output.offset - 10 ) ; offset++ ) {
+
+               /* Check for jump instruction */
+               if ( lzx->output.data[offset] != 0xe8 )
+                       continue;
+
+               /* Translate jump target */
+               target = ( ( int32_t * ) &lzx->output.data[ offset + 1 ] );
+               if ( *target >= 0 ) {
+                       if ( *target < LZX_WIM_MAGIC_FILESIZE )
+                               *target -= offset;
+               } else {
+                       if ( *target >= -( ( int32_t ) offset ) )
+                               *target += LZX_WIM_MAGIC_FILESIZE;
+               }
+               offset += sizeof ( *target );
+       }
+}
+
+/**
+ * Decompress LZX-compressed data
+ *
+ * @v data             Compressed data
+ * @v len              Length of compressed data
+ * @v buf              Decompression buffer, or NULL
+ * @ret out_len                Length of decompressed data, or negative error
+ */
+ssize_t lzx_decompress ( const void *data, size_t len, void *buf ) {
+       struct lzx lzx;
+       unsigned int i;
+       int rc;
+
+       /* Sanity check */
+       if ( len % 2 ) {
+               DBG ( "LZX cannot handle odd-length input data\n" );
+               return -1;
+       }
+
+       /* Initialise global state, if required */
+       if ( ! lzx_position_base[ LZX_POSITION_SLOTS - 1 ] ) {
+               for ( i = 1 ; i < LZX_POSITION_SLOTS ; i++ ) {
+                       lzx_position_base[i] =
+                               ( lzx_position_base[i-1] +
+                                 ( 1 << lzx_footer_bits ( i - 1 ) ) );
+               }
+       }
+
+       /* Initialise decompressor */
+       memset ( &lzx, 0, sizeof ( lzx ) );
+       lzx.input.data = data;
+       lzx.input.len = len;
+       lzx.output.data = buf;
+       for ( i = 0 ; i < LZX_REPEATED_OFFSETS ; i++ )
+               lzx.repeated_offset[i] = 1;
+
+       /* Process blocks */
+       while ( lzx.input.offset < lzx.input.len ) {
+
+               /* Process block header */
+               if ( ( rc = lzx_block_header ( &lzx ) ) != 0 )
+                       return rc;
+
+               /* Process block contents */
+               if ( lzx.block_type == LZX_BLOCK_UNCOMPRESSED ) {
+
+                       /* Copy uncompressed data */
+                       if ( ( rc = lzx_uncompressed ( &lzx ) ) != 0 )
+                               return rc;
+
+               } else {
+
+                       /* Process token stream */
+                       while ( lzx.output.offset < lzx.output.threshold ) {
+                               if ( ( rc = lzx_token ( &lzx ) ) != 0 )
+                                       return rc;
+                       }
+               }
+       }
+
+       /* Postprocess to undo E8 jump compression */
+       if ( lzx.output.data )
+               lzx_translate_jumps ( &lzx );
+
+       return lzx.output.offset;
+}
diff --git a/GRUB2/grub-2.04/grub-core/ventoy/lzx.h b/GRUB2/grub-2.04/grub-core/ventoy/lzx.h
new file mode 100644 (file)
index 0000000..6a4ed8f
--- /dev/null
@@ -0,0 +1,179 @@
+#ifndef _LZX_H
+#define _LZX_H
+
+/*
+ * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ */
+
+/**
+ * @file
+ *
+ * LZX decompression
+ *
+ */
+
+#include "huffman.h"
+
+/** Number of aligned offset codes */
+#define LZX_ALIGNOFFSET_CODES 8
+
+/** Aligned offset code length (in bits) */
+#define LZX_ALIGNOFFSET_BITS 3
+
+/** Number of pretree codes */
+#define LZX_PRETREE_CODES 20
+
+/** Pretree code length (in bits) */
+#define LZX_PRETREE_BITS 4
+
+/** Number of literal main codes */
+#define LZX_MAIN_LIT_CODES 256
+
+/** Number of position slots */
+#define LZX_POSITION_SLOTS 30
+
+/** Number of main codes */
+#define LZX_MAIN_CODES ( LZX_MAIN_LIT_CODES + ( 8 * LZX_POSITION_SLOTS ) )
+
+/** Number of length codes */
+#define LZX_LENGTH_CODES 249
+
+/** Block type length (in bits) */
+#define LZX_BLOCK_TYPE_BITS 3
+
+/** Default block length */
+#define LZX_DEFAULT_BLOCK_LEN 32768
+
+/** Number of repeated offsets */
+#define LZX_REPEATED_OFFSETS 3
+
+/** Don't ask */
+#define LZX_WIM_MAGIC_FILESIZE 12000000
+
+/** Block types */
+enum lzx_block_type {
+       /** Verbatim block */
+       LZX_BLOCK_VERBATIM = 1,
+       /** Aligned offset block */
+       LZX_BLOCK_ALIGNOFFSET = 2,
+       /** Uncompressed block */
+       LZX_BLOCK_UNCOMPRESSED = 3,
+};
+
+/** An LZX input stream */
+struct lzx_input_stream {
+       /** Data */
+       const uint8_t *data;
+       /** Length */
+       size_t len;
+       /** Offset within stream */
+       size_t offset;
+};
+
+/** An LZX output stream */
+struct lzx_output_stream {
+       /** Data, or NULL */
+       uint8_t *data;
+       /** Offset within stream */
+       size_t offset;
+       /** End of current block within stream */
+       size_t threshold;
+};
+
+/** LZX decompressor */
+struct lzx {
+       /** Input stream */
+       struct lzx_input_stream input;
+       /** Output stream */
+       struct lzx_output_stream output;
+       /** Accumulator */
+       uint32_t accumulator;
+       /** Number of bits in accumulator */
+       unsigned int bits;
+       /** Block type */
+       enum lzx_block_type block_type;
+       /** Repeated offsets */
+       unsigned int repeated_offset[LZX_REPEATED_OFFSETS];
+
+       /** Aligned offset Huffman alphabet */
+       struct huffman_alphabet alignoffset;
+       /** Aligned offset raw symbols
+        *
+        * Must immediately follow the aligned offset Huffman
+        * alphabet.
+        */
+       huffman_raw_symbol_t alignoffset_raw[LZX_ALIGNOFFSET_CODES];
+       /** Aligned offset code lengths */
+       uint8_t alignoffset_lengths[LZX_ALIGNOFFSET_CODES];
+
+       /** Pretree Huffman alphabet */
+       struct huffman_alphabet pretree;
+       /** Pretree raw symbols
+        *
+        * Must immediately follow the pretree Huffman alphabet.
+        */
+       huffman_raw_symbol_t pretree_raw[LZX_PRETREE_CODES];
+       /** Preetree code lengths */
+       uint8_t pretree_lengths[LZX_PRETREE_CODES];
+
+       /** Main Huffman alphabet */
+       struct huffman_alphabet main;
+       /** Main raw symbols
+        *
+        * Must immediately follow the main Huffman alphabet.
+        */
+       huffman_raw_symbol_t main_raw[LZX_MAIN_CODES];
+       /** Main code lengths */
+       struct {
+               /** Literals */
+               uint8_t literals[LZX_MAIN_LIT_CODES];
+               /** Remaining symbols */
+               uint8_t remainder[ LZX_MAIN_CODES - LZX_MAIN_LIT_CODES ];
+       } __attribute__ (( packed )) main_lengths;
+
+       /** Length Huffman alphabet */
+       struct huffman_alphabet length;
+       /** Length raw symbols
+        *
+        * Must immediately follow the length Huffman alphabet.
+        */
+       huffman_raw_symbol_t length_raw[LZX_LENGTH_CODES];
+       /** Length code lengths */
+       uint8_t length_lengths[LZX_LENGTH_CODES];
+};
+
+/**
+ * Calculate number of footer bits for a given position slot
+ *
+ * @v position_slot    Position slot
+ * @ret footer_bits    Number of footer bits
+ */
+static inline unsigned int lzx_footer_bits ( unsigned int position_slot ) {
+
+       if ( position_slot < 2 ) {
+               return 0;
+       } else if ( position_slot < 38 ) {
+               return ( ( position_slot / 2 ) - 1 );
+       } else {
+               return 17;
+       }
+}
+
+extern ssize_t lzx_decompress ( const void *data, size_t len, void *buf );
+
+#endif /* _LZX_H */
diff --git a/GRUB2/grub-2.04/grub-core/ventoy/ventoy.c b/GRUB2/grub-2.04/grub-core/ventoy/ventoy.c
new file mode 100644 (file)
index 0000000..15b7f54
--- /dev/null
@@ -0,0 +1,1153 @@
+/******************************************************************************
+ * ventoy.c 
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/disk.h>
+#include <grub/device.h>
+#include <grub/term.h>
+#include <grub/partition.h>
+#include <grub/file.h>
+#include <grub/normal.h>
+#include <grub/extcmd.h>
+#include <grub/datetime.h>
+#include <grub/i18n.h>
+#include <grub/net.h>
+#include <grub/time.h>
+#include <grub/ventoy.h>
+#include "ventoy_def.h"
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+int g_ventoy_debug = 0;
+static int g_efi_os = 0xFF;
+initrd_info *g_initrd_img_list = NULL;
+initrd_info *g_initrd_img_tail = NULL;
+int g_initrd_img_count = 0;
+int g_valid_initrd_count = 0;
+
+static grub_file_t g_old_file;
+
+char g_img_swap_tmp_buf[1024];
+
+img_info *g_ventoy_img_list = NULL;
+int g_ventoy_img_count = 0;
+
+img_iterator_node g_img_iterator_head;
+
+grub_uint8_t g_ventoy_break_level = 0;
+grub_uint8_t g_ventoy_debug_level = 0;
+grub_uint8_t *g_ventoy_cpio_buf = NULL;
+grub_uint32_t g_ventoy_cpio_size = 0;
+cpio_newc_header *g_ventoy_initrd_head = NULL;
+grub_uint8_t *g_ventoy_runtime_buf = NULL;
+
+ventoy_grub_param g_grub_param;
+
+ventoy_guid  g_ventoy_guid = VENTOY_GUID;
+
+ventoy_img_chunk_list g_img_chunk_list;
+
+void ventoy_debug(const char *fmt, ...)
+{
+    va_list args;
+
+    va_start (args, fmt);
+    grub_vprintf (fmt, args);
+    va_end (args);
+}
+
+int ventoy_is_efi_os(void)
+{
+    if (g_efi_os > 1)
+    {
+        g_efi_os = (grub_strstr(GRUB_PLATFORM, "efi")) ? 1 : 0;
+    }
+
+    return g_efi_os;
+}
+
+static int ventoy_string_check(const char *str, grub_char_check_func check)
+{
+    if (!str)
+    {
+        return 0;
+    }
+    
+    for ( ; *str; str++)
+    {
+        if (!check(*str))
+        {
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+
+static grub_ssize_t ventoy_fs_read(grub_file_t file, char *buf, grub_size_t len)
+{
+    grub_memcpy(buf, (char *)file->data + file->offset, len);
+    return len;
+}
+
+static grub_err_t ventoy_fs_close(grub_file_t file)
+{
+    grub_file_close(g_old_file);
+    grub_free(file->data);
+
+    file->device = 0;
+    file->name = 0;
+
+    return 0;
+}
+
+static grub_file_t ventoy_wrapper_open(grub_file_t rawFile, enum grub_file_type type)
+{
+    int len;
+    grub_file_t file;
+    static struct grub_fs vtoy_fs =
+    {
+        .name = "vtoy",
+        .fs_dir = 0,
+        .fs_open = 0,
+        .fs_read = ventoy_fs_read,
+        .fs_close = ventoy_fs_close,
+        .fs_label = 0,
+        .next = 0
+    };
+
+    if (type != 52)
+    {
+        return rawFile;
+    }
+
+    file = (grub_file_t)grub_zalloc(sizeof (*file));
+    if (!file)
+    {
+        return 0;
+    }
+
+    file->data = grub_malloc(rawFile->size + 4096);
+    if (!file->data)
+    {
+        return 0;
+    }
+
+    grub_file_read(rawFile, file->data, rawFile->size);
+    len = ventoy_fill_data(4096, (char *)file->data + rawFile->size);
+
+    g_old_file = rawFile;
+    
+    file->size = rawFile->size + len;
+    file->device = rawFile->device;
+    file->fs = &vtoy_fs;
+    file->not_easily_seekable = 1;
+
+    return file;
+}
+
+static int ventoy_check_decimal_var(const char *name, long *value)
+{
+    const char *value_str = NULL;
+    
+    value_str = grub_env_get(name);
+    if (NULL == value_str)
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "Variable %s not found", name);
+    }
+
+    if (!ventoy_is_decimal(value_str))
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "Variable %s value '%s' is not an integer", name, value_str);
+    }
+
+    *value = grub_strtol(value_str, NULL, 10);
+
+    return GRUB_ERR_NONE;
+}
+
+static grub_err_t ventoy_cmd_debug(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    if (argc != 1)
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {on|off}", cmd_raw_name);
+    }
+
+    if (0 == grub_strcmp(args[0], "on"))
+    {
+        g_ventoy_debug = 1;
+        grub_env_set("vtdebug_flag", "debug");
+    }
+    else
+    {
+        g_ventoy_debug = 0;
+        grub_env_set("vtdebug_flag", "");
+    }
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+static grub_err_t ventoy_cmd_break(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    (void)ctxt;
+
+    if (argc < 1 || (args[0][0] != '0' && args[0][0] != '1'))
+    {
+        grub_printf("Usage: %s {level} [debug]\r\n", cmd_raw_name);
+        grub_printf(" level:\r\n");
+        grub_printf("    01/11: busybox / (+cat log)\r\n");
+        grub_printf("    02/12: initrd / (+cat log)\r\n");
+        grub_printf("    03/13: hook / (+cat log)\r\n");
+        grub_printf("\r\n");
+        grub_printf(" debug:\r\n");
+        grub_printf("    0: debug is on\r\n");
+        grub_printf("    1: debug is off\r\n");
+        grub_printf("\r\n");
+        VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+    }
+
+    g_ventoy_break_level = (grub_uint8_t)grub_strtoul(args[0], NULL, 16);
+
+    if (argc > 1 && grub_strtoul(args[1], NULL, 10) > 0)
+    {
+        g_ventoy_debug_level = 1;
+    }
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+static grub_err_t ventoy_cmd_incr(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    long value_long = 0;
+    char buf[32];
+    
+    if ((argc != 2) || (!ventoy_is_decimal(args[1])))
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {Variable} {Int}", cmd_raw_name);
+    }
+
+    if (GRUB_ERR_NONE != ventoy_check_decimal_var(args[0], &value_long))
+    {
+        return grub_errno;
+    }
+
+    value_long += grub_strtol(args[1], NULL, 10);
+
+    grub_snprintf(buf, sizeof(buf), "%ld", value_long);
+    grub_env_set(args[0], buf);
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+static grub_err_t ventoy_cmd_is_udf(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    int i;
+    int rc = 1;
+    grub_file_t file;
+    grub_uint8_t buf[32];
+    
+    (void)ctxt;
+    (void)argc;
+    (void)args;
+
+    if (argc != 1)
+    {
+        return rc;
+    }
+
+    file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
+    if (file == NULL)
+    {
+        debug("failed to open file <%s> for udf check\n", args[0]);
+        return 1;
+    }
+
+    for (i = 16; i < 32; i++)
+    {
+        grub_file_seek(file, i * 2048);
+        grub_file_read(file, buf, sizeof(buf));
+        if (buf[0] == 255)
+        {
+            break;
+        }
+    }
+
+    i++;
+    grub_file_seek(file, i * 2048);
+    grub_file_read(file, buf, sizeof(buf));
+
+    if (grub_memcmp(buf + 1, "BEA01", 5) == 0)
+    {
+        i++;
+        grub_file_seek(file, i * 2048);
+        grub_file_read(file, buf, sizeof(buf));
+
+        if (grub_memcmp(buf + 1, "NSR02", 5) == 0 ||
+            grub_memcmp(buf + 1, "NSR03", 5) == 0)
+        {
+            rc = 0;
+        }
+    }
+
+    grub_file_close(file); 
+
+    debug("ISO UDF: %s\n", rc ? "NO" : "YES");
+    
+    return rc;
+}
+
+static grub_err_t ventoy_cmd_cmp(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    long value_long1 = 0;
+    long value_long2 = 0;
+    
+    if ((argc != 3) || (!ventoy_is_decimal(args[0])) || (!ventoy_is_decimal(args[2])))
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {Int1} { eq|ne|gt|lt|ge|le } {Int2}", cmd_raw_name);
+    }
+
+    value_long1 = grub_strtol(args[0], NULL, 10);
+    value_long2 = grub_strtol(args[2], NULL, 10);
+
+    if (0 == grub_strcmp(args[1], "eq"))
+    {
+        grub_errno = (value_long1 == value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE;
+    }
+    else if (0 == grub_strcmp(args[1], "ne"))
+    {
+        grub_errno = (value_long1 != value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE;
+    }
+    else if (0 == grub_strcmp(args[1], "gt"))
+    {
+        grub_errno = (value_long1 > value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE;
+    }
+    else if (0 == grub_strcmp(args[1], "lt"))
+    {
+        grub_errno = (value_long1 < value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE;
+    }
+    else if (0 == grub_strcmp(args[1], "ge"))
+    {
+        grub_errno = (value_long1 >= value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE;
+    }
+    else if (0 == grub_strcmp(args[1], "le"))
+    {
+        grub_errno = (value_long1 <= value_long2) ? GRUB_ERR_NONE : GRUB_ERR_TEST_FAILURE;
+    }
+    else
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {Int1} { eq ne gt lt ge le } {Int2}", cmd_raw_name);
+    }
+    
+    return grub_errno;
+}
+
+static grub_err_t ventoy_cmd_device(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    char *pos = NULL;
+    char buf[128] = {0};
+    
+    if (argc != 2)
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s path var", cmd_raw_name);
+    }
+
+    grub_strncpy(buf, (args[0][0] == '(') ? args[0] + 1 : args[0], sizeof(buf) - 1);
+    pos = grub_strstr(buf, ",");
+    if (pos)
+    {
+        *pos = 0;
+    }
+
+    grub_env_set(args[1], buf);
+    
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+static grub_err_t ventoy_cmd_check_compatible(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    int i;
+    char buf[256];
+    grub_disk_t disk;
+    char *pos = NULL;
+    const char *files[] = { "ventoy.dat", "VENTOY.DAT" };
+
+    (void)ctxt;
+    
+    if (argc != 1)
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s  (loop)", cmd_raw_name);
+    }
+
+    for (i = 0; i < (int)ARRAY_SIZE(files); i++)
+    {
+        grub_snprintf(buf, sizeof(buf) - 1, "[ -e %s/%s ]", args[0], files[i]);
+        if (0 == grub_script_execute_sourcecode(buf))
+        {
+            debug("file %s exist, ventoy_compatible YES\n", buf);
+            grub_env_set("ventoy_compatible", "YES");
+            VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+        }
+        else
+        {
+            debug("file %s NOT exist\n", buf);
+        }
+    }
+    
+    grub_snprintf(buf, sizeof(buf) - 1, "%s", args[0][0] == '(' ? (args[0] + 1) : args[0]);
+    pos = grub_strstr(buf, ")");
+    if (pos)
+    {
+        *pos = 0;
+    }
+
+    disk = grub_disk_open(buf);
+    if (disk)
+    {
+        grub_disk_read(disk, 16 << 2, 0, 1024, g_img_swap_tmp_buf);
+        grub_disk_close(disk);
+        
+        g_img_swap_tmp_buf[703] = 0;
+        for (i = 319; i < 703; i++)
+        {
+            if (g_img_swap_tmp_buf[i] == 'V' &&
+                0 == grub_strncmp(g_img_swap_tmp_buf + i, VENTOY_COMPATIBLE_STR, VENTOY_COMPATIBLE_STR_LEN))
+            {
+                debug("Ventoy compatible string exist at  %d, ventoy_compatible YES\n", i);
+                grub_env_set("ventoy_compatible", "YES");
+                VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+            }
+        }
+    }
+    else
+    {
+        debug("failed to open disk <%s>\n", buf);
+    }
+
+    grub_env_set("ventoy_compatible", "NO");
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+static int ventoy_cmp_img(img_info *img1, img_info *img2)
+{
+    char *s1, *s2;
+    int c1 = 0;
+    int c2 = 0;
+
+    for (s1 = img1->name, s2 = img2->name; *s1 && *s2; s1++, s2++)
+    {
+        c1 = *s1;
+        c2 = *s2;
+
+        if (grub_islower(c1))
+        {
+            c1 = c1 - 'a' + 'A';
+        }
+        
+        if (grub_islower(c2))
+        {
+            c2 = c2 - 'a' + 'A';
+        }
+
+        if (c1 != c2)
+        {
+            break;
+        }
+    }
+
+    return (c1 - c2);
+}
+
+static void ventoy_swap_img(img_info *img1, img_info *img2)
+{
+    grub_memcpy(g_img_swap_tmp_buf, img1->name, sizeof(img1->name));
+    grub_memcpy(img1->name, img2->name, sizeof(img1->name));
+    grub_memcpy(img2->name, g_img_swap_tmp_buf, sizeof(img1->name));
+    
+    grub_memcpy(g_img_swap_tmp_buf, img1->path, sizeof(img1->path));
+    grub_memcpy(img1->path, img2->path, sizeof(img1->path));
+    grub_memcpy(img2->path, g_img_swap_tmp_buf, sizeof(img1->path));
+}
+
+static int ventoy_img_name_valid(const char *filename, grub_size_t namelen)
+{
+    grub_size_t i;
+
+    for (i = 0; i < namelen; i++)
+    {
+        if (filename[i] == ' ' || filename[i] == '\t')
+        {
+            return 0;
+        }
+
+        if ((grub_uint8_t)(filename[i]) >= 127)
+        {
+            return 0;
+        }
+    }
+
+    return 1;
+}
+
+static int ventoy_colect_img_files(const char *filename, const struct grub_dirhook_info *info, void *data)
+{
+    grub_size_t len;
+    img_info *img;
+    img_info *tail;
+    img_iterator_node *new_node;
+    img_iterator_node *node = (img_iterator_node *)data;
+
+    len = grub_strlen(filename);
+    
+    if (info->dir)
+    {
+        if ((len == 1 && filename[0] == '.') ||
+            (len == 2 && filename[0] == '.' && filename[1] == '.'))
+        {
+            return 0;
+        }
+
+        new_node = grub_malloc(sizeof(img_iterator_node));
+        if (new_node)
+        {
+            new_node->tail = node->tail;
+            grub_snprintf(new_node->dir, sizeof(new_node->dir), "%s%s/", node->dir, filename);
+
+            new_node->next = g_img_iterator_head.next;
+            g_img_iterator_head.next = new_node;
+        }
+    }
+    else
+    {
+        debug("Find a file %s\n", filename);
+
+        if ((len > 4) && (0 == grub_strcasecmp(filename + len - 4, ".iso")))
+        {
+            if (!ventoy_img_name_valid(filename, len))
+            {
+                return 0;
+            }
+        
+            img = grub_zalloc(sizeof(img_info));
+            if (img)
+            {
+                grub_snprintf(img->name, sizeof(img->name), "%s", filename);
+                grub_snprintf(img->path, sizeof(img->path), "%s%s", node->dir, filename);
+                
+                if (g_ventoy_img_list)
+                {
+                    tail = *(node->tail);
+                    img->prev = tail;
+                    tail->next = img;
+                }
+                else
+                {
+                    g_ventoy_img_list = img;
+                }
+                
+                *((img_info **)(node->tail)) = img;
+                g_ventoy_img_count++;
+
+                debug("Add %s%s to list %d\n", node->dir, filename, g_ventoy_img_count);
+            }
+        }
+    }
+
+    return 0;
+}
+
+int ventoy_fill_data(grub_uint32_t buflen, char *buffer)
+{
+    int len = GRUB_UINT_MAX;
+    const char *value = NULL;
+    char name[32] = {0};
+    char plat[32] = {0};
+    char guidstr[32] = {0};
+    ventoy_guid guid = VENTOY_GUID;
+    const char *fmt1 = NULL;
+    const char *fmt2 = NULL;
+    const char *fmt3 = NULL;    
+    grub_uint32_t *puint = (grub_uint32_t *)name;
+    grub_uint32_t *puint2 = (grub_uint32_t *)plat;
+    const char fmtdata[]={ 0x39, 0x35, 0x25, 0x00, 0x35, 0x00, 0x23, 0x30, 0x30, 0x30, 0x30, 0x66, 0x66, 0x00 };
+    const char fmtcode[]={
+        0x22, 0x0A, 0x2B, 0x20, 0x68, 0x62, 0x6F, 0x78, 0x20, 0x7B, 0x0A, 0x20, 0x20, 0x74, 0x6F, 0x70,
+        0x20, 0x3D, 0x20, 0x25, 0x73, 0x0A, 0x20, 0x20, 0x6C, 0x65, 0x66, 0x74, 0x20, 0x3D, 0x20, 0x25,
+        0x73, 0x0A, 0x20, 0x20, 0x2B, 0x20, 0x6C, 0x61, 0x62, 0x65, 0x6C, 0x20, 0x7B, 0x74, 0x65, 0x78,
+        0x74, 0x20, 0x3D, 0x20, 0x22, 0x25, 0x73, 0x20, 0x25, 0x73, 0x25, 0x73, 0x22, 0x20, 0x63, 0x6F,
+        0x6C, 0x6F, 0x72, 0x20, 0x3D, 0x20, 0x22, 0x25, 0x73, 0x22, 0x20, 0x61, 0x6C, 0x69, 0x67, 0x6E,
+        0x20, 0x3D, 0x20, 0x22, 0x6C, 0x65, 0x66, 0x74, 0x22, 0x7D, 0x0A, 0x7D, 0x0A, 0x22, 0x00
+    };
+
+    grub_memset(name, 0, sizeof(name));
+    puint[0] = grub_swap_bytes32(0x56454e54);
+    puint[3] = grub_swap_bytes32(0x4f4e0000);
+    puint[2] = grub_swap_bytes32(0x45525349);
+    puint[1] = grub_swap_bytes32(0x4f595f56);
+    value = ventoy_get_env(name);
+
+    grub_memset(name, 0, sizeof(name));
+    puint[1] = grub_swap_bytes32(0x5f544f50);
+    puint[0] = grub_swap_bytes32(0x56544c45);
+    fmt1 = ventoy_get_env(name);
+    if (!fmt1)
+    {
+        fmt1 = fmtdata;
+    }
+    
+    grub_memset(name, 0, sizeof(name));
+    puint[1] = grub_swap_bytes32(0x5f4c4654);
+    puint[0] = grub_swap_bytes32(0x56544c45);
+    fmt2 = ventoy_get_env(name);
+    
+    grub_memset(name, 0, sizeof(name));
+    puint[1] = grub_swap_bytes32(0x5f434c52);
+    puint[0] = grub_swap_bytes32(0x56544c45);
+    fmt3 = ventoy_get_env(name);
+
+    grub_memcpy(guidstr, &guid, sizeof(guid));
+
+    #if defined (GRUB_MACHINE_EFI)
+    puint2[0] = grub_swap_bytes32(0x55454649);
+    #else
+    puint2[0] = grub_swap_bytes32(0x42494f53);
+    #endif
+
+    /* Easter egg :) It will be appreciated if you reserve it, but NOT mandatory. */
+    #pragma GCC diagnostic push
+    #pragma GCC diagnostic ignored "-Wformat-nonliteral"
+    len = grub_snprintf(buffer, buflen, fmtcode, 
+                        fmt1 ? fmt1 : fmtdata, 
+                        fmt2 ? fmt2 : fmtdata + 4, 
+                        value ? value : "", plat, guidstr, 
+                        fmt3 ? fmt3 : fmtdata + 6);
+    #pragma GCC diagnostic pop
+
+    grub_memset(name, 0, sizeof(name));
+    puint[0] = grub_swap_bytes32(0x76746f79);
+    puint[2] = grub_swap_bytes32(0x656e7365);
+    puint[1] = grub_swap_bytes32(0x5f6c6963);
+    ventoy_set_env(name, guidstr);
+
+    return len;
+}
+
+static grub_err_t ventoy_cmd_list_img(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    grub_fs_t fs;
+    grub_device_t dev = NULL;
+    img_info *cur = NULL;
+    img_info *tail = NULL;
+    char *device_name = NULL;
+    char buf[32];
+    img_iterator_node *node = NULL;
+    
+    (void)ctxt;
+
+    if (argc != 2)
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {device} {cntvar}", cmd_raw_name);
+    }
+
+    if (g_ventoy_img_list || g_ventoy_img_count)
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "Must clear image before list");
+    }
+
+    device_name = grub_file_get_device_name(args[0]);
+    if (!device_name)
+    {
+        goto fail;
+    }
+
+    dev = grub_device_open(device_name);
+    if (!dev)
+    {
+        goto fail;        
+    }
+
+    fs = grub_fs_probe(dev);
+    if (!fs)
+    {
+        goto fail;
+    }
+
+    grub_memset(&g_img_iterator_head, 0, sizeof(g_img_iterator_head));
+
+    g_img_iterator_head.tail = &tail;
+    grub_strcpy(g_img_iterator_head.dir, "/");    
+
+    fs->fs_dir(dev, "/", ventoy_colect_img_files, &g_img_iterator_head);
+
+    while (g_img_iterator_head.next)
+    {
+        node = g_img_iterator_head.next;
+        g_img_iterator_head.next = node->next;
+
+        fs->fs_dir(dev, node->dir, ventoy_colect_img_files, node);
+        grub_free(node);
+    }
+
+    /* sort image list by image name */
+    for (cur = g_ventoy_img_list; cur; cur = cur->next)
+    {
+        for (tail = cur->next; tail; tail = tail->next)
+        {
+            if (ventoy_cmp_img(cur, tail) > 0)
+            {
+                ventoy_swap_img(cur, tail);
+            }
+        }
+    }
+
+    grub_snprintf(buf, sizeof(buf), "%d", g_ventoy_img_count);
+    grub_env_set(args[1], buf);
+
+fail:
+
+    check_free(device_name, grub_free);
+    check_free(dev, grub_device_close);
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+
+static grub_err_t ventoy_cmd_clear_img(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    img_info *next = NULL;
+    img_info *cur = g_ventoy_img_list;
+
+    (void)ctxt;
+    (void)argc;
+    (void)args;
+
+    while (cur)
+    {
+        next = cur->next;
+        grub_free(cur);
+        cur = next;
+    }
+    
+    g_ventoy_img_list = NULL;
+    g_ventoy_img_count = 0;
+    
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+static grub_err_t ventoy_cmd_img_name(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    long img_id = 0;
+    img_info *cur = g_ventoy_img_list;
+
+    (void)ctxt;
+    
+    if (argc != 2 || (!ventoy_is_decimal(args[0])))
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {imageID} {var}", cmd_raw_name);
+    }
+
+    img_id = grub_strtol(args[0], NULL, 10);
+    if (img_id >= g_ventoy_img_count)
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "No such many images %ld %ld", img_id, g_ventoy_img_count);
+    }
+
+    debug("Find image %ld name \n", img_id);
+
+    while (cur && img_id > 0)
+    {
+        img_id--;
+        cur = cur->next;
+    }
+
+    if (!cur)
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "No such many images");
+    }
+
+    debug("image name is %s\n", cur->name);
+
+    grub_env_set(args[1], cur->name);
+    
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+static grub_err_t ventoy_cmd_chosen_img_path(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    const char *name = NULL;
+    img_info *cur = g_ventoy_img_list;
+
+    (void)ctxt;
+    
+    if (argc != 1)
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s {var}", cmd_raw_name);
+    }
+
+    name = grub_env_get("chosen");
+
+    while (cur)
+    {
+        if (0 == grub_strcmp(name, cur->name))
+        {
+            grub_env_set(args[0], cur->path);
+            break;
+        }
+        cur = cur->next;
+    }
+
+    if (!cur)
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "No such image");
+    }
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+static int ventoy_get_disk_guid(const char *filename, grub_uint8_t *guid)
+{
+    grub_disk_t disk;
+    char *device_name;
+    char *pos;
+    char *pos2;
+    
+    device_name = grub_file_get_device_name(filename);
+    if (!device_name)
+    {
+        return 1;
+    }
+
+    pos = device_name;
+    if (pos[0] == '(')
+    {
+        pos++;
+    }
+
+    pos2 = grub_strstr(pos, ",");
+    if (!pos2)
+    {
+        pos2 = grub_strstr(pos, ")");
+    }
+    
+    if (pos2)
+    {
+        *pos2 = 0;
+    }
+
+    disk = grub_disk_open(pos);
+    if (disk)
+    {
+        grub_disk_read(disk, 0, 0x180, 16, guid);
+        grub_disk_close(disk);
+    }
+    else
+    {
+        return 1;
+    }
+
+    grub_free(device_name);
+    return 0;
+}
+
+grub_uint32_t ventoy_get_iso_boot_catlog(grub_file_t file)
+{
+    eltorito_descriptor desc;
+
+    grub_memset(&desc, 0, sizeof(desc));
+    grub_file_seek(file, 17 * 2048);
+    grub_file_read(file, &desc, sizeof(desc));
+
+    if (desc.type != 0 || desc.version != 1)
+    {
+        return 0;
+    }
+
+    if (grub_strncmp((char *)desc.id, "CD001", 5) != 0 ||
+        grub_strncmp((char *)desc.system_id, "EL TORITO SPECIFICATION", 23) != 0)
+    {
+        return 0;
+    }
+
+    return desc.sector;    
+}
+
+int ventoy_has_efi_eltorito(grub_file_t file, grub_uint32_t sector)
+{
+    int i;
+    grub_uint8_t buf[512];
+
+    grub_file_seek(file, sector * 2048);
+    grub_file_read(file, buf, sizeof(buf));
+
+    if (buf[0] == 0x01 && buf[1] == 0xEF)
+    {
+        debug("%s efi eltorito in Validation Entry\n", file->name);
+        return 1;
+    }
+
+    for (i = 64; i < (int)sizeof(buf); i += 32)
+    {
+        if ((buf[i] == 0x90 || buf[i] == 0x91) && buf[i + 1] == 0xEF)
+        {
+            debug("%s efi eltorito offset %d 0x%02x\n", file->name, i, buf[i]);
+            return 1;
+        }
+    }
+
+    debug("%s does not contain efi eltorito\n", file->name);
+    return 0;
+}
+
+void ventoy_fill_os_param(grub_file_t file, ventoy_os_param *param)
+{
+    char *pos;
+    grub_uint32_t i;
+    grub_uint8_t  chksum = 0;
+    grub_disk_t   disk;
+
+    disk = file->device->disk;
+    grub_memcpy(&param->guid, &g_ventoy_guid, sizeof(ventoy_guid));
+
+    param->vtoy_disk_size = disk->total_sectors * (1 << disk->log_sector_size);
+    param->vtoy_disk_part_id = disk->partition->number + 1;
+
+    if (grub_strcmp(file->fs->name, "exfat") == 0)
+    {
+        param->vtoy_disk_part_type = 0;
+    }
+    else if (grub_strcmp(file->fs->name, "ntfs") == 0)
+    {
+        param->vtoy_disk_part_type = 1;
+    }
+    else
+    {
+        param->vtoy_disk_part_type = 0xFFFF;
+    }
+
+    pos = grub_strstr(file->name, "/");
+    if (!pos)
+    {
+        pos = file->name;
+    }
+
+    grub_snprintf(param->vtoy_img_path, sizeof(param->vtoy_img_path), "%s", pos);
+    
+    ventoy_get_disk_guid(file->name, param->vtoy_disk_guid);
+
+    param->vtoy_img_size = file->size;
+
+    param->vtoy_reserved[0] = g_ventoy_break_level;
+    param->vtoy_reserved[1] = g_ventoy_debug_level;
+
+    /* calculate checksum */
+    for (i = 0; i < sizeof(ventoy_os_param); i++)
+    {
+        chksum += *((grub_uint8_t *)param + i);
+    }
+    param->chksum = (grub_uint8_t)(0x100 - chksum);
+
+    return;
+}
+
+static grub_err_t ventoy_cmd_img_sector(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    grub_file_t file;
+    
+    (void)ctxt;
+    (void)argc;
+
+    file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
+    if (!file)
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't open file %s\n", args[0]); 
+    }
+
+    if (g_img_chunk_list.chunk)
+    {
+        grub_free(g_img_chunk_list.chunk);
+    }
+
+    /* get image chunk data */
+    grub_memset(&g_img_chunk_list, 0, sizeof(g_img_chunk_list));
+    g_img_chunk_list.chunk = grub_malloc(sizeof(ventoy_img_chunk) * DEFAULT_CHUNK_NUM);
+    if (NULL == g_img_chunk_list.chunk)
+    {
+        return grub_error(GRUB_ERR_OUT_OF_MEMORY, "Can't allocate image chunk memoty\n");
+    }
+    
+    g_img_chunk_list.max_chunk = DEFAULT_CHUNK_NUM;
+    g_img_chunk_list.cur_chunk = 0;
+
+    debug("get fat file chunk part start:%llu\n", (unsigned long long)file->device->disk->partition->start);
+    grub_fat_get_file_chunk(file->device->disk->partition->start, file, &g_img_chunk_list);
+
+    grub_file_close(file);
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+static grub_err_t ventoy_cmd_dump_img_sector(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    grub_uint32_t i;
+    ventoy_img_chunk *cur;
+    
+    (void)ctxt;
+    (void)argc;
+    (void)args;
+
+    for (i = 0; i < g_img_chunk_list.cur_chunk; i++)
+    {
+        cur = g_img_chunk_list.chunk + i;
+        grub_printf("image:[%u - %u]   <==>  disk:[%llu - %llu]\n", 
+            cur->img_start_sector, cur->img_end_sector,
+            (unsigned long long)cur->disk_start_sector, (unsigned long long)cur->disk_end_sector
+            );
+    }
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+grub_file_t ventoy_grub_file_open(enum grub_file_type type, const char *fmt, ...)
+{
+    va_list ap;
+    grub_file_t file;
+    char fullpath[256] = {0};
+
+    va_start (ap, fmt);
+    grub_vsnprintf(fullpath, 255, fmt, ap);
+    va_end (ap);
+
+    file = grub_file_open(fullpath, type);
+    if (!file)
+    {
+        debug("grub_file_open failed <%s>\n", fullpath);
+        grub_errno = 0;
+    }
+
+    return file;
+}
+
+int ventoy_is_file_exist(const char *fmt, ...)
+{
+    va_list ap;
+    int len;
+    char *pos = NULL;
+    char buf[256] = {0};
+
+    grub_snprintf(buf, sizeof(buf), "%s", "[ -f ");
+    pos = buf + 5;
+
+    va_start (ap, fmt);
+    len = grub_vsnprintf(pos, 255, fmt, ap);
+    va_end (ap);
+
+    grub_strncpy(pos + len, " ]", 2);
+
+    debug("script exec %s\n", buf);
+
+    if (0 == grub_script_execute_sourcecode(buf))
+    {
+        return 1;
+    }
+
+    return 0;
+}
+
+static int ventoy_env_init(void)
+{
+    char buf[64];
+
+    grub_env_set("vtdebug_flag", "");
+
+    ventoy_filt_register(0, ventoy_wrapper_open);
+    
+    g_grub_param.grub_env_get = grub_env_get;
+    grub_snprintf(buf, sizeof(buf), "%p", &g_grub_param);
+    grub_env_set("env_param", buf);
+
+    return 0;
+}
+
+static cmd_para ventoy_cmds[] = 
+{
+    { "vt_incr",  ventoy_cmd_incr,  0, NULL, "{Var} {INT}",   "Increase integer variable",    NULL },
+    { "vt_debug", ventoy_cmd_debug, 0, NULL, "{on|off}",   "turn debug on/off",    NULL },
+    { "vtdebug", ventoy_cmd_debug, 0, NULL, "{on|off}",   "turn debug on/off",    NULL },
+    { "vtbreak", ventoy_cmd_break, 0, NULL, "{level}",   "set debug break",    NULL },
+    { "vt_cmp",   ventoy_cmd_cmp, 0, NULL, "{Int1} { eq|ne|gt|lt|ge|le } {Int2}", "Comare two integers", NULL },
+    { "vt_device", ventoy_cmd_device, 0, NULL, "path var", "", NULL },
+    { "vt_check_compatible",   ventoy_cmd_check_compatible, 0, NULL, "", "", NULL },
+    { "vt_list_img", ventoy_cmd_list_img, 0, NULL, "{device} {cntvar}", "find all iso file in device", NULL },
+    { "vt_clear_img", ventoy_cmd_clear_img, 0, NULL, "", "clear image list", NULL },
+    { "vt_img_name", ventoy_cmd_img_name, 0, NULL, "{imageID} {var}", "get image name", NULL },
+    { "vt_chosen_img_path", ventoy_cmd_chosen_img_path, 0, NULL, "{var}", "get chosen img path", NULL },
+    { "vt_img_sector", ventoy_cmd_img_sector, 0, NULL, "{imageName}", "", NULL },
+    { "vt_dump_img_sector", ventoy_cmd_dump_img_sector, 0, NULL, "", "", NULL },
+    { "vt_load_cpio", ventoy_cmd_load_cpio, 0, NULL, "", "", NULL },
+
+    { "vt_is_udf", ventoy_cmd_is_udf, 0, NULL, "", "", NULL },
+    
+    { "vt_linux_parse_initrd_isolinux", ventoy_cmd_isolinux_initrd_collect, 0, NULL, "{cfgfile}", "", NULL },
+    { "vt_linux_parse_initrd_grub", ventoy_cmd_grub_initrd_collect, 0, NULL, "{cfgfile}", "", NULL },
+    { "vt_linux_specify_initrd_file", ventoy_cmd_specify_initrd_file, 0, NULL, "", "", NULL },
+    { "vt_linux_clear_initrd", ventoy_cmd_clear_initrd_list, 0, NULL, "", "", NULL },
+    { "vt_linux_dump_initrd", ventoy_cmd_dump_initrd_list, 0, NULL, "", "", NULL },
+    { "vt_linux_initrd_count", ventoy_cmd_initrd_count, 0, NULL, "", "", NULL },
+    { "vt_linux_locate_initrd", ventoy_cmd_linux_locate_initrd, 0, NULL, "", "", NULL },
+    { "vt_linux_chain_data", ventoy_cmd_linux_chain_data, 0, NULL, "", "", NULL },
+
+    { "vt_windows_reset",      ventoy_cmd_wimdows_reset, 0, NULL, "", "", NULL },
+    { "vt_windows_locate_wim", ventoy_cmd_wimdows_locate_wim, 0, NULL, "", "", NULL },
+    { "vt_windows_chain_data", ventoy_cmd_windows_chain_data, 0, NULL, "", "", NULL },
+    
+    { "vt_load_plugin", ventoy_cmd_load_plugin, 0, NULL, "", "", NULL },
+};
+
+
+
+GRUB_MOD_INIT(ventoy)
+{
+    grub_uint32_t i;
+    cmd_para *cur = NULL;
+
+    ventoy_env_init();
+    
+    for (i = 0; i < ARRAY_SIZE(ventoy_cmds); i++)
+    {
+        cur = ventoy_cmds + i;
+        cur->cmd = grub_register_extcmd(cur->name, cur->func, cur->flags, 
+                                        cur->summary, cur->description, cur->parser);
+    }
+}
+
+GRUB_MOD_FINI(ventoy)
+{
+    grub_uint32_t i;
+    
+    for (i = 0; i < ARRAY_SIZE(ventoy_cmds); i++)
+    {
+        grub_unregister_extcmd(ventoy_cmds[i].cmd);
+    }
+}
+
diff --git a/GRUB2/grub-2.04/grub-core/ventoy/ventoy_def.h b/GRUB2/grub-2.04/grub-core/ventoy/ventoy_def.h
new file mode 100644 (file)
index 0000000..4b28bc2
--- /dev/null
@@ -0,0 +1,507 @@
+/******************************************************************************
+ * ventoy_def.h
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __VENTOY_DEF_H__
+#define __VENTOY_DEF_H__
+
+#define JSON_SUCCESS    0
+#define JSON_FAILED     1
+#define JSON_NOT_FOUND  2
+
+#define ulonglong  unsigned long long
+
+#define vtoy_to_upper(c) (((char)(c) >= 'a' && (char)(c) <= 'z') ? ((char)(c) - 'a' + 'A') : (char)(c))
+
+#define VENTOY_CMD_RETURN(err)  grub_errno = (err); return (err)
+#define VENTOY_FILE_TYPE    (GRUB_FILE_TYPE_NO_DECOMPRESS | GRUB_FILE_TYPE_LINUX_INITRD)
+
+#define ventoy_env_op1(op, a) grub_env_##op(a)
+#define ventoy_env_op2(op, a, b) grub_env_##op((a), (b))
+
+#define ventoy_get_env(key)         ventoy_env_op1(get, key)
+#define ventoy_set_env(key, val)    ventoy_env_op2(set, key, val)
+
+typedef struct ventoy_initrd_ctx
+{
+    const char *path_prefix;
+    const char *dir_prefix;
+}ventoy_initrd_ctx;
+
+typedef struct cmd_para
+{
+    const char *name;
+    grub_extcmd_func_t func;
+    grub_command_flags_t flags;
+    const struct grub_arg_option *parser;
+    
+    const char *summary;
+    const char *description;
+
+    grub_extcmd_t cmd;
+}cmd_para;
+
+#define ventoy_align(value, align)  (((value) + ((align) - 1)) & (~((align) - 1)))
+
+#pragma pack(1)
+typedef struct cpio_newc_header 
+{
+    char  c_magic[6];
+    char  c_ino[8];
+    char  c_mode[8];
+    char  c_uid[8];
+    char  c_gid[8];
+    char  c_nlink[8];
+    char  c_mtime[8];
+    char  c_filesize[8];
+    char  c_devmajor[8];
+    char  c_devminor[8];
+    char  c_rdevmajor[8];
+    char  c_rdevminor[8];
+    char  c_namesize[8];
+    char  c_check[8];
+}cpio_newc_header;
+#pragma pack()
+
+
+#define cmd_raw_name ctxt->extcmd->cmd->name
+#define check_free(p, func) if (p) { func(p); p = NULL; }
+
+typedef int (*grub_char_check_func)(int c);
+#define ventoy_is_decimal(str)  ventoy_string_check(str, grub_isdigit)
+
+
+// El Torito Boot Record Volume Descriptor
+#pragma pack(1)
+typedef struct eltorito_descriptor
+{
+    grub_uint8_t type;
+    grub_uint8_t id[5];
+    grub_uint8_t version;
+    grub_uint8_t system_id[32];
+    grub_uint8_t reserved[32];
+    grub_uint32_t sector;
+}eltorito_descriptor;
+
+typedef struct ventoy_iso9660_override
+{
+    grub_uint32_t first_sector;
+    grub_uint32_t first_sector_be;
+    grub_uint32_t size;
+    grub_uint32_t size_be;
+}ventoy_iso9660_override;
+
+typedef struct ventoy_udf_override
+{
+    grub_uint32_t length;
+    grub_uint32_t position;
+}ventoy_udf_override;
+
+#pragma pack()
+
+
+typedef struct img_info
+{
+    char path[512];
+    char name[256];
+
+    struct img_info *next;
+    struct img_info *prev;
+}img_info;
+
+typedef struct img_iterator_node
+{
+    struct img_iterator_node *next;
+    img_info **tail;
+    char dir[400];
+}img_iterator_node;
+
+typedef struct initrd_info
+{
+    char name[256];
+
+    grub_uint64_t offset;
+    grub_uint64_t size;
+
+    grub_uint8_t  iso_type; // 0: iso9660  1:udf
+    grub_uint32_t udf_start_block;
+    
+    grub_uint64_t override_offset;
+    grub_uint32_t override_length;
+    char          override_data[32];
+
+    struct initrd_info *next;
+    struct initrd_info *prev;
+}initrd_info;
+
+extern initrd_info *g_initrd_img_list;
+extern initrd_info *g_initrd_img_tail;
+extern int g_initrd_img_count;
+extern int g_valid_initrd_count;
+
+extern img_info *g_ventoy_img_list;
+extern int g_ventoy_img_count;
+
+extern grub_uint8_t *g_ventoy_cpio_buf;
+extern grub_uint32_t g_ventoy_cpio_size;
+extern cpio_newc_header *g_ventoy_initrd_head;
+extern grub_uint8_t *g_ventoy_runtime_buf;
+
+extern ventoy_guid  g_ventoy_guid;
+
+extern ventoy_img_chunk_list g_img_chunk_list;
+
+extern int g_ventoy_debug;
+void ventoy_debug(const char *fmt, ...);
+#define debug(fmt, ...) if (g_ventoy_debug) ventoy_debug("[VTOY]: "fmt, __VA_ARGS__)
+
+
+
+#define FLAG_HEADER_RESERVED          0x00000001
+#define FLAG_HEADER_COMPRESSION       0x00000002
+#define FLAG_HEADER_READONLY          0x00000004
+#define FLAG_HEADER_SPANNED           0x00000008
+#define FLAG_HEADER_RESOURCE_ONLY     0x00000010
+#define FLAG_HEADER_METADATA_ONLY     0x00000020
+#define FLAG_HEADER_WRITE_IN_PROGRESS 0x00000040
+#define FLAG_HEADER_RP_FIX            0x00000080 // reparse point fixup
+#define FLAG_HEADER_COMPRESS_RESERVED 0x00010000
+#define FLAG_HEADER_COMPRESS_XPRESS   0x00020000
+#define FLAG_HEADER_COMPRESS_LZX      0x00040000
+
+#define RESHDR_FLAG_FREE 0x01
+#define RESHDR_FLAG_METADATA 0x02
+#define RESHDR_FLAG_COMPRESSED 0x04
+#define RESHDR_FLAG_SPANNED 0x08
+
+#pragma pack(1)
+
+/* A WIM resource header */
+typedef struct wim_resource_header 
+{
+    grub_uint64_t size_in_wim:56; /* Compressed length */
+    grub_uint64_t flags:8;        /* flags  */
+    grub_uint64_t offset;         /* Offset */
+    grub_uint64_t raw_size;       /* Uncompressed length */
+}wim_resource_header;
+
+/* WIM resource header length mask */
+#define WIM_RESHDR_ZLEN_MASK 0x00ffffffffffffffULL
+
+/* WIM resource header flags */
+typedef enum wim_resource_header_flags 
+{
+    WIM_RESHDR_METADATA = ( 0x02ULL << 56 ),       /* Resource contains metadata */
+    WIM_RESHDR_COMPRESSED = ( 0x04ULL << 56 ),     /* Resource is compressed */
+    WIM_RESHDR_PACKED_STREAMS = ( 0x10ULL << 56 ), /* Resource is compressed using packed streams */
+}wim_resource_header_flags;
+
+#define WIM_HEAD_SIGNATURE   "MSWIM\0\0"
+
+/* WIM header */
+typedef struct wim_header 
+{
+    grub_uint8_t signature[8];          /* Signature */
+    grub_uint32_t header_len;           /* Header length */
+    grub_uint32_t version;              /* Verson */
+    grub_uint32_t flags;                /* Flags */
+    grub_uint32_t chunk_len;            /* Chunk length */
+    grub_uint8_t guid[16];              /* GUID */
+    grub_uint16_t part;                 /* Part number */
+    grub_uint16_t parts;                /* Total number of parts */
+    grub_uint32_t images;               /* number of images */
+    wim_resource_header lookup;    /* Lookup table */
+    wim_resource_header xml;       /* XML data */
+    wim_resource_header metadata;  /* Boot metadata */
+    grub_uint32_t boot_index;           /* Boot index */
+    wim_resource_header integrity; /* Integrity table */
+    grub_uint8_t reserved[60];          /* Reserved */
+} wim_header;
+
+/* WIM header flags */
+typedef enum wim_header_flags 
+{
+    WIM_HDR_XPRESS = 0x00020000, /* WIM uses Xpress compresson */
+    WIM_HDR_LZX = 0x00040000,    /* WIM uses LZX compression */
+}wim_header_flags;
+
+/* A WIM file hash */
+typedef struct wim_hash 
+{
+    /* SHA-1 hash */
+    grub_uint8_t sha1[20];
+}wim_hash;
+
+/* A WIM lookup table entry */
+typedef struct wim_lookup_entry 
+{
+    wim_resource_header resource; /* Resource header */
+    grub_uint16_t part;           /* Part number */
+    grub_uint32_t refcnt;         /* Reference count */
+    wim_hash hash;                /* Hash */
+}wim_lookup_entry;
+
+/* WIM chunk length */
+#define WIM_CHUNK_LEN 32768
+
+/* A WIM chunk buffer */
+typedef struct wim_chunk_buffer 
+{
+    grub_uint8_t data[WIM_CHUNK_LEN]; /*Data */
+}wim_chunk_buffer;
+
+/* Security data */
+typedef struct wim_security_header 
+{
+    grub_uint32_t len;   /* Length */
+    grub_uint32_t count; /* Number of entries */
+}wim_security_header;
+
+/* Directory entry */
+typedef struct wim_directory_entry 
+{
+    grub_uint64_t len;                 /* Length */
+    grub_uint32_t attributes;     /* Attributes */
+    grub_uint32_t security;       /* Security ID */
+    grub_uint64_t subdir;              /* Subdirectory offset */
+    grub_uint8_t reserved1[16];   /* Reserved */
+    grub_uint64_t created;             /* Creation time */
+    grub_uint64_t accessed;            /* Last access time */
+    grub_uint64_t written;             /* Last written time */
+    wim_hash hash;                /* Hash */
+    grub_uint8_t reserved2[12];   /* Reserved */
+    grub_uint16_t streams;        /* Streams */
+    grub_uint16_t short_name_len; /* Short name length */
+    grub_uint16_t name_len;       /* Name length */
+}wim_directory_entry;
+
+/** Normal file */
+#define WIM_ATTR_NORMAL 0x00000080UL
+
+/** No security information exists for this file */
+#define WIM_NO_SECURITY 0xffffffffUL
+
+#pragma pack()
+
+
+typedef struct wim_tail
+{
+    grub_uint32_t wim_raw_size;
+    grub_uint32_t wim_align_size;
+
+    grub_uint8_t  iso_type;
+    grub_uint64_t file_offset;
+    grub_uint32_t udf_start_block;
+    grub_uint64_t fe_entry_size_offset;
+    grub_uint64_t override_offset;
+    grub_uint32_t override_len;
+    grub_uint8_t  override_data[32];
+
+    wim_header wim_header;
+
+    wim_hash bin_hash;
+    grub_uint32_t jump_exe_len;
+    grub_uint8_t *jump_bin_data;
+    grub_uint32_t bin_raw_len;
+    grub_uint32_t bin_align_len;
+
+    grub_uint8_t *new_meta_data;
+    grub_uint32_t new_meta_len;
+    grub_uint32_t new_meta_align_len;
+
+    grub_uint8_t *new_lookup_data;
+    grub_uint32_t new_lookup_len;
+    grub_uint32_t new_lookup_align_len;
+}wim_tail;
+
+
+
+typedef enum _JSON_TYPE
+{
+    JSON_TYPE_NUMBER = 0,
+    JSON_TYPE_STRING,
+    JSON_TYPE_BOOL,
+    JSON_TYPE_ARRAY,
+    JSON_TYPE_OBJECT,
+    JSON_TYPE_NULL,
+    JSON_TYPE_BUTT
+}JSON_TYPE;
+
+
+typedef struct _VTOY_JSON
+{
+    struct _VTOY_JSON *pstPrev;
+    struct _VTOY_JSON *pstNext;
+    struct _VTOY_JSON *pstChild;
+
+    JSON_TYPE enDataType;
+    union 
+    {
+        char  *pcStrVal;
+        int   iNumVal;
+        grub_uint64_t lValue;
+    }unData;
+
+    char *pcName;
+}VTOY_JSON;
+
+typedef struct _JSON_PARSE
+{
+    char *pcKey;
+    void *pDataBuf;
+    grub_uint32_t  uiBufSize;
+}JSON_PARSE;
+
+#define JSON_NEW_ITEM(pstJson, ret) \
+{ \
+    (pstJson) = (VTOY_JSON *)grub_zalloc(sizeof(VTOY_JSON)); \
+    if (NULL == (pstJson)) \
+    { \
+        json_debug("Failed to alloc memory for json.\n"); \
+        return (ret); \
+    } \
+}
+
+typedef int (*ventoy_plugin_entry_pf)(VTOY_JSON *json, const char *isodisk);
+
+typedef struct plugin_entry
+{
+    const char *key;
+    ventoy_plugin_entry_pf entryfunc;
+}plugin_entry;
+
+
+void ventoy_fill_os_param(grub_file_t file, ventoy_os_param *param);
+grub_err_t ventoy_cmd_isolinux_initrd_collect(grub_extcmd_context_t ctxt, int argc, char **args);
+grub_err_t ventoy_cmd_grub_initrd_collect(grub_extcmd_context_t ctxt, int argc, char **args);
+grub_err_t ventoy_cmd_specify_initrd_file(grub_extcmd_context_t ctxt, int argc, char **args);
+grub_err_t ventoy_cmd_dump_initrd_list(grub_extcmd_context_t ctxt, int argc, char **args);
+grub_err_t ventoy_cmd_clear_initrd_list(grub_extcmd_context_t ctxt, int argc, char **args);
+grub_uint32_t ventoy_get_iso_boot_catlog(grub_file_t file);
+int ventoy_has_efi_eltorito(grub_file_t file, grub_uint32_t sector);
+grub_err_t ventoy_cmd_linux_chain_data(grub_extcmd_context_t ctxt, int argc, char **args);
+grub_err_t ventoy_cmd_linux_locate_initrd(grub_extcmd_context_t ctxt, int argc, char **args);
+grub_err_t ventoy_cmd_initrd_count(grub_extcmd_context_t ctxt, int argc, char **args);
+grub_err_t ventoy_cmd_load_cpio(grub_extcmd_context_t ctxt, int argc, char **args);
+int ventoy_cpio_newc_fill_head(void *buf, int filesize, void *filedata, const char *name);
+grub_file_t ventoy_grub_file_open(enum grub_file_type type, const char *fmt, ...);
+int ventoy_is_file_exist(const char *fmt, ...);
+int ventoy_fill_data(grub_uint32_t buflen, char *buffer);
+grub_err_t ventoy_cmd_load_plugin(grub_extcmd_context_t ctxt, int argc, char **args);
+grub_err_t ventoy_cmd_wimdows_reset(grub_extcmd_context_t ctxt, int argc, char **args);
+grub_err_t ventoy_cmd_wimdows_locate_wim(grub_extcmd_context_t ctxt, int argc, char **args);
+grub_err_t ventoy_cmd_windows_chain_data(grub_extcmd_context_t ctxt, int argc, char **args);
+
+VTOY_JSON *vtoy_json_find_item
+(
+    VTOY_JSON *pstJson,
+    JSON_TYPE  enDataType,
+    const char *szKey
+);
+int vtoy_json_parse_value
+(
+    char *pcNewStart,
+    char *pcRawStart,
+    VTOY_JSON *pstJson, 
+    const char *pcData,
+    const char **ppcEnd
+);
+VTOY_JSON * vtoy_json_create(void);
+int vtoy_json_parse(VTOY_JSON *pstJson, const char *szJsonData);
+
+int vtoy_json_scan_parse
+(
+    const VTOY_JSON    *pstJson,
+    grub_uint32_t       uiParseNum,
+    JSON_PARSE         *pstJsonParse
+);
+
+int vtoy_json_scan_array
+(
+     VTOY_JSON *pstJson, 
+     const char *szKey, 
+     VTOY_JSON **ppstArrayItem
+);
+
+int vtoy_json_scan_array_ex
+(
+     VTOY_JSON *pstJson, 
+     const char *szKey, 
+     VTOY_JSON **ppstArrayItem
+);
+int vtoy_json_scan_object
+(
+     VTOY_JSON *pstJson, 
+     const char *szKey, 
+    VTOY_JSON **ppstObjectItem
+);
+int vtoy_json_get_int
+(
+    VTOY_JSON *pstJson, 
+    const char *szKey, 
+    int *piValue
+);
+int vtoy_json_get_uint
+(
+    VTOY_JSON *pstJson, 
+    const char *szKey, 
+    grub_uint32_t *puiValue
+);
+int vtoy_json_get_uint64
+(
+    VTOY_JSON *pstJson, 
+    const char *szKey, 
+    grub_uint64_t *pui64Value
+);
+int vtoy_json_get_bool
+(
+    VTOY_JSON *pstJson,
+    const char *szKey, 
+    grub_uint8_t *pbValue
+);
+int vtoy_json_get_string
+(
+     VTOY_JSON *pstJson, 
+     const char *szKey, 
+     grub_uint32_t  uiBufLen,
+     char *pcBuf
+);
+const char * vtoy_json_get_string_ex(VTOY_JSON *pstJson,  const char *szKey);
+int vtoy_json_destroy(VTOY_JSON *pstJson);
+
+
+grub_uint32_t CalculateCrc32
+(
+    const void       *Buffer,
+    grub_uint32_t          Length,
+    grub_uint32_t          InitValue
+);
+
+static inline int ventoy_isspace (int c)
+{
+    return (c == '\n' || c == '\r' || c == ' ' || c == '\t');
+}
+
+static inline int ventoy_is_word_end(int c)
+{
+    return (c == 0 || c == ',' || ventoy_isspace(c));    
+}
+
+#endif /* __VENTOY_DEF_H__ */
+
diff --git a/GRUB2/grub-2.04/grub-core/ventoy/ventoy_json.c b/GRUB2/grub-2.04/grub-core/ventoy/ventoy_json.c
new file mode 100644 (file)
index 0000000..8a4e8b1
--- /dev/null
@@ -0,0 +1,736 @@
+/******************************************************************************
+ * ventoy_json.c 
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/disk.h>
+#include <grub/device.h>
+#include <grub/term.h>
+#include <grub/partition.h>
+#include <grub/file.h>
+#include <grub/normal.h>
+#include <grub/extcmd.h>
+#include <grub/datetime.h>
+#include <grub/i18n.h>
+#include <grub/net.h>
+#include <grub/time.h>
+#include <grub/ventoy.h>
+#include "ventoy_def.h"
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static void json_debug(const char *fmt, ...)
+{
+    va_list args;
+
+    va_start (args, fmt);
+    grub_vprintf (fmt, args);
+    va_end (args);
+
+    grub_printf("\n");
+}
+
+static void vtoy_json_free(VTOY_JSON *pstJsonHead)
+{
+    VTOY_JSON *pstNext = NULL;
+
+    while (NULL != pstJsonHead)
+    {
+        pstNext = pstJsonHead->pstNext;
+        if ((pstJsonHead->enDataType < JSON_TYPE_BUTT) && (NULL != pstJsonHead->pstChild))
+        {
+            vtoy_json_free(pstJsonHead->pstChild);
+        }
+
+        grub_free(pstJsonHead);
+        pstJsonHead = pstNext;
+    }
+
+    return;
+}
+
+static char *vtoy_json_skip(const char *pcData)
+{
+    while ((NULL != pcData) && ('\0' != *pcData) && (*pcData <= 32))
+    {
+        pcData++;
+    }
+
+    return (char *)pcData;
+}
+
+VTOY_JSON *vtoy_json_find_item
+(
+    VTOY_JSON *pstJson,
+    JSON_TYPE  enDataType,
+    const char *szKey
+)
+{
+    while (NULL != pstJson)
+    {
+        if ((enDataType == pstJson->enDataType) && 
+            (0 == grub_strcmp(szKey, pstJson->pcName)))
+        {
+            return pstJson;
+        }
+        pstJson = pstJson->pstNext;
+    }
+    
+    return NULL;
+}
+
+static int vtoy_json_parse_number
+(
+    VTOY_JSON *pstJson, 
+    const char *pcData,
+    const char **ppcEnd
+)
+{
+    unsigned long Value;
+
+    Value = grub_strtoul(pcData, (char **)ppcEnd, 10);
+    if (*ppcEnd == pcData)
+    {
+        json_debug("Failed to parse json number %s.", pcData);
+        return JSON_FAILED;
+    }
+
+    pstJson->enDataType = JSON_TYPE_NUMBER;
+    pstJson->unData.lValue = Value;
+    
+    return JSON_SUCCESS;
+}
+
+static int vtoy_json_parse_string
+(
+    char *pcNewStart,
+    char *pcRawStart,
+    VTOY_JSON *pstJson, 
+    const char *pcData,
+    const char **ppcEnd
+)
+{
+    grub_uint32_t uiLen = 0;
+    const char *pcPos = NULL;
+    const char *pcTmp = pcData + 1;
+    
+    *ppcEnd = pcData;
+
+    if ('\"' != *pcData)
+    {
+        return JSON_FAILED;
+    }
+
+    pcPos = grub_strchr(pcTmp, '\"');
+    if ((NULL == pcPos) || (pcPos < pcTmp))
+    {
+        json_debug("Invalid string %s.", pcData);
+        return JSON_FAILED;
+    }
+
+    *ppcEnd = pcPos + 1;
+    uiLen = (grub_uint32_t)(unsigned long)(pcPos - pcTmp);    
+    
+    pstJson->enDataType = JSON_TYPE_STRING;
+    pstJson->unData.pcStrVal = pcNewStart + (pcTmp - pcRawStart);
+    pstJson->unData.pcStrVal[uiLen] = '\0';
+    
+    return JSON_SUCCESS;
+}
+
+static int vtoy_json_parse_array
+(
+    char *pcNewStart,
+    char *pcRawStart,
+    VTOY_JSON *pstJson, 
+    const char *pcData,
+    const char **ppcEnd
+)
+{
+    int Ret = JSON_SUCCESS;
+    VTOY_JSON *pstJsonChild = NULL;
+    VTOY_JSON *pstJsonItem = NULL;
+    const char *pcTmp = pcData + 1;
+
+    *ppcEnd = pcData;
+    pstJson->enDataType = JSON_TYPE_ARRAY;
+
+    if ('[' != *pcData)
+    {
+        return JSON_FAILED;
+    }
+
+    pcTmp = vtoy_json_skip(pcTmp);
+
+    if (']' == *pcTmp)
+    {
+        *ppcEnd = pcTmp + 1;
+        return JSON_SUCCESS;
+    }
+
+    JSON_NEW_ITEM(pstJson->pstChild, JSON_FAILED);
+
+    Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJson->pstChild, pcTmp, ppcEnd);
+    if (JSON_SUCCESS != Ret)
+    {
+        json_debug("Failed to parse array child.");
+        return JSON_FAILED;
+    }
+
+    pstJsonChild = pstJson->pstChild;
+    pcTmp = vtoy_json_skip(*ppcEnd);
+    while ((NULL != pcTmp) && (',' == *pcTmp))
+    {
+        JSON_NEW_ITEM(pstJsonItem, JSON_FAILED);
+        pstJsonChild->pstNext = pstJsonItem;
+        pstJsonItem->pstPrev = pstJsonChild;
+        pstJsonChild = pstJsonItem;
+
+        Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);
+        if (JSON_SUCCESS != Ret)
+        {
+            json_debug("Failed to parse array child.");
+            return JSON_FAILED;
+        }
+        pcTmp = vtoy_json_skip(*ppcEnd);
+    }
+
+    if ((NULL != pcTmp) && (']' == *pcTmp))
+    {
+        *ppcEnd = pcTmp + 1;
+        return JSON_SUCCESS;
+    }
+    else
+    {
+        *ppcEnd = pcTmp;
+        return JSON_FAILED;
+    }
+}
+
+static int vtoy_json_parse_object
+(
+    char *pcNewStart,
+    char *pcRawStart,
+    VTOY_JSON *pstJson, 
+    const char *pcData,
+    const char **ppcEnd
+)
+{
+    int Ret = JSON_SUCCESS;
+    VTOY_JSON *pstJsonChild = NULL;
+    VTOY_JSON *pstJsonItem = NULL;
+    const char *pcTmp = pcData + 1;
+
+    *ppcEnd = pcData;
+    pstJson->enDataType = JSON_TYPE_OBJECT;
+
+    if ('{' != *pcData)
+    {
+        return JSON_FAILED;
+    }
+
+    pcTmp = vtoy_json_skip(pcTmp);
+    if ('}' == *pcTmp)
+    {
+        *ppcEnd = pcTmp + 1;
+        return JSON_SUCCESS;
+    }
+
+    JSON_NEW_ITEM(pstJson->pstChild, JSON_FAILED);
+
+    Ret = vtoy_json_parse_string(pcNewStart, pcRawStart, pstJson->pstChild, pcTmp, ppcEnd);
+    if (JSON_SUCCESS != Ret)
+    {
+        json_debug("Failed to parse array child.");
+        return JSON_FAILED;
+    }
+
+    pstJsonChild = pstJson->pstChild;
+    pstJsonChild->pcName = pstJsonChild->unData.pcStrVal;
+    pstJsonChild->unData.pcStrVal = NULL;
+
+    pcTmp = vtoy_json_skip(*ppcEnd);
+    if ((NULL == pcTmp) || (':' != *pcTmp))
+    {
+        *ppcEnd = pcTmp;
+        return JSON_FAILED;
+    }
+
+    Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);
+    if (JSON_SUCCESS != Ret)
+    {
+        json_debug("Failed to parse array child.");
+        return JSON_FAILED;
+    }
+
+    pcTmp = vtoy_json_skip(*ppcEnd);
+    while ((NULL != pcTmp) && (',' == *pcTmp))
+    {
+        JSON_NEW_ITEM(pstJsonItem, JSON_FAILED);
+        pstJsonChild->pstNext = pstJsonItem;
+        pstJsonItem->pstPrev = pstJsonChild;
+        pstJsonChild = pstJsonItem;
+
+        Ret = vtoy_json_parse_string(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);
+        if (JSON_SUCCESS != Ret)
+        {
+            json_debug("Failed to parse array child.");
+            return JSON_FAILED;
+        }
+
+        pcTmp = vtoy_json_skip(*ppcEnd);
+        pstJsonChild->pcName = pstJsonChild->unData.pcStrVal;
+        pstJsonChild->unData.pcStrVal = NULL;
+        if ((NULL == pcTmp) || (':' != *pcTmp))
+        {
+            *ppcEnd = pcTmp;
+            return JSON_FAILED;
+        }
+
+        Ret = vtoy_json_parse_value(pcNewStart, pcRawStart, pstJsonChild, vtoy_json_skip(pcTmp + 1), ppcEnd);
+        if (JSON_SUCCESS != Ret)
+        {
+            json_debug("Failed to parse array child.");
+            return JSON_FAILED;
+        }
+
+        pcTmp = vtoy_json_skip(*ppcEnd);
+    }
+
+    if ((NULL != pcTmp) && ('}' == *pcTmp))
+    {
+        *ppcEnd = pcTmp + 1;
+        return JSON_SUCCESS;
+    }
+    else
+    {
+        *ppcEnd = pcTmp;
+        return JSON_FAILED;
+    }
+}
+
+int vtoy_json_parse_value
+(
+    char *pcNewStart,
+    char *pcRawStart,
+    VTOY_JSON *pstJson, 
+    const char *pcData,
+    const char **ppcEnd
+)
+{
+    pcData = vtoy_json_skip(pcData);
+    
+    switch (*pcData)
+    {
+        case 'n':
+        {
+            if (0 == grub_strncmp(pcData, "null", 4))
+            {
+                pstJson->enDataType = JSON_TYPE_NULL;
+                *ppcEnd = pcData + 4;
+                return JSON_SUCCESS;
+            }
+            break;
+        }
+        case 'f':
+        {
+            if (0 == grub_strncmp(pcData, "false", 5))
+            {
+                pstJson->enDataType = JSON_TYPE_BOOL;
+                pstJson->unData.lValue = 0;
+                *ppcEnd = pcData + 5;
+                return JSON_SUCCESS;
+            }
+            break;
+        }
+        case 't':
+        {
+            if (0 == grub_strncmp(pcData, "true", 4))
+            {
+                pstJson->enDataType = JSON_TYPE_BOOL;
+                pstJson->unData.lValue = 1;
+                *ppcEnd = pcData + 4;
+                return JSON_SUCCESS;
+            }
+            break;
+        }
+        case '\"':
+        {
+            return vtoy_json_parse_string(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd);
+        }
+        case '[':
+        {
+            return vtoy_json_parse_array(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd);
+        }
+        case '{':
+        {
+            return vtoy_json_parse_object(pcNewStart, pcRawStart, pstJson, pcData, ppcEnd);
+        }
+        case '-':
+        {
+            return vtoy_json_parse_number(pstJson, pcData, ppcEnd);
+        }
+        default :
+        {
+            if (*pcData >= '0' && *pcData <= '9')
+            {
+                return vtoy_json_parse_number(pstJson, pcData, ppcEnd);
+            }
+        }
+    }
+
+    *ppcEnd = pcData;
+    json_debug("Invalid json data %u.", (grub_uint8_t)(*pcData));
+    return JSON_FAILED;
+}
+
+VTOY_JSON * vtoy_json_create(void)
+{
+    VTOY_JSON *pstJson = NULL;
+
+    pstJson = (VTOY_JSON *)grub_zalloc(sizeof(VTOY_JSON));
+    if (NULL == pstJson)
+    {
+        return NULL;
+    }
+    
+    return pstJson;
+}
+
+int vtoy_json_parse(VTOY_JSON *pstJson, const char *szJsonData)
+{
+    grub_uint32_t uiMemSize = 0;
+    int Ret = JSON_SUCCESS;
+    char *pcNewBuf = NULL;
+    const char *pcEnd = NULL;
+
+    uiMemSize = grub_strlen(szJsonData) + 1;
+    pcNewBuf = (char *)grub_malloc(uiMemSize);
+    if (NULL == pcNewBuf)
+    {
+        json_debug("Failed to alloc new buf.");
+        return JSON_FAILED;
+    }
+    grub_memcpy(pcNewBuf, szJsonData, uiMemSize);
+    pcNewBuf[uiMemSize - 1] = 0;
+
+    Ret = vtoy_json_parse_value(pcNewBuf, (char *)szJsonData, pstJson, szJsonData, &pcEnd);
+    if (JSON_SUCCESS != Ret)
+    {
+        json_debug("Failed to parse json data %s start=%p, end=%p:%s.", 
+                    szJsonData, szJsonData, pcEnd, pcEnd);
+        return JSON_FAILED;
+    }
+
+    return JSON_SUCCESS;
+}
+
+int vtoy_json_scan_parse
+(
+    const VTOY_JSON    *pstJson,
+    grub_uint32_t       uiParseNum,
+    JSON_PARSE         *pstJsonParse
+)
+{   
+    grub_uint32_t i = 0;
+    const VTOY_JSON *pstJsonCur = NULL;
+    JSON_PARSE *pstCurParse = NULL;
+
+    for (pstJsonCur = pstJson; NULL != pstJsonCur; pstJsonCur = pstJsonCur->pstNext)
+    {
+        if ((JSON_TYPE_OBJECT == pstJsonCur->enDataType) ||
+            (JSON_TYPE_ARRAY == pstJsonCur->enDataType))
+        {
+            continue;
+        }
+
+        for (i = 0, pstCurParse = NULL; i < uiParseNum; i++)
+        {
+            if (0 == grub_strcmp(pstJsonParse[i].pcKey, pstJsonCur->pcName))
+            {   
+                pstCurParse = pstJsonParse + i;
+                break;
+            }
+        }
+
+        if (NULL == pstCurParse)
+        {
+            continue;
+        }
+    
+        switch (pstJsonCur->enDataType)
+        {
+            case JSON_TYPE_NUMBER:
+            {
+                if (sizeof(grub_uint32_t) == pstCurParse->uiBufSize)
+                {
+                    *(grub_uint32_t *)(pstCurParse->pDataBuf) = (grub_uint32_t)pstJsonCur->unData.lValue;
+                }
+                else if (sizeof(grub_uint16_t) == pstCurParse->uiBufSize)
+                {
+                    *(grub_uint16_t *)(pstCurParse->pDataBuf) = (grub_uint16_t)pstJsonCur->unData.lValue;
+                }
+                else if (sizeof(grub_uint8_t) == pstCurParse->uiBufSize)
+                {
+                    *(grub_uint8_t *)(pstCurParse->pDataBuf) = (grub_uint8_t)pstJsonCur->unData.lValue;
+                }
+                else if ((pstCurParse->uiBufSize > sizeof(grub_uint64_t)))
+                {
+                    grub_snprintf((char *)pstCurParse->pDataBuf, pstCurParse->uiBufSize, "%llu", 
+                        (unsigned long long)(pstJsonCur->unData.lValue));
+                }
+                else
+                {
+                    json_debug("Invalid number data buf size %u.", pstCurParse->uiBufSize);
+                }
+                break;
+            }
+            case JSON_TYPE_STRING:
+            {
+                grub_strncpy((char *)pstCurParse->pDataBuf, pstJsonCur->unData.pcStrVal, pstCurParse->uiBufSize);
+                break;
+            }
+            case JSON_TYPE_BOOL:
+            {
+                *(grub_uint8_t *)(pstCurParse->pDataBuf) = (pstJsonCur->unData.lValue) > 0 ? 1 : 0;
+                break;
+            }
+            default :
+            {
+                break;
+            }
+        }
+    }
+
+    return JSON_SUCCESS;
+}
+
+int vtoy_json_scan_array
+(
+     VTOY_JSON *pstJson, 
+     const char *szKey, 
+     VTOY_JSON **ppstArrayItem
+)
+{
+    VTOY_JSON *pstJsonItem = NULL;
+    
+    pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_ARRAY, szKey);
+    if (NULL == pstJsonItem)
+    {
+        json_debug("Key %s is not found in json data.", szKey);
+        return JSON_NOT_FOUND;
+    }
+
+    *ppstArrayItem = pstJsonItem;
+
+    return JSON_SUCCESS;
+}
+
+int vtoy_json_scan_array_ex
+(
+     VTOY_JSON *pstJson, 
+     const char *szKey, 
+     VTOY_JSON **ppstArrayItem
+)
+{
+    VTOY_JSON *pstJsonItem = NULL;
+    
+    pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_ARRAY, szKey);
+    if (NULL == pstJsonItem)
+    {
+        json_debug("Key %s is not found in json data.", szKey);
+        return JSON_NOT_FOUND;
+    }
+    
+    *ppstArrayItem = pstJsonItem->pstChild;
+
+    return JSON_SUCCESS;
+}
+
+int vtoy_json_scan_object
+(
+     VTOY_JSON *pstJson, 
+     const char *szKey, 
+     VTOY_JSON **ppstObjectItem
+)
+{
+    VTOY_JSON *pstJsonItem = NULL;
+
+    pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_OBJECT, szKey);
+    if (NULL == pstJsonItem)
+    {
+        json_debug("Key %s is not found in json data.", szKey);
+        return JSON_NOT_FOUND;
+    }
+
+    *ppstObjectItem = pstJsonItem;
+
+    return JSON_SUCCESS;
+}
+
+int vtoy_json_get_int
+(
+    VTOY_JSON *pstJson, 
+    const char *szKey, 
+    int *piValue
+)
+{
+    VTOY_JSON *pstJsonItem = NULL;
+    
+    pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey);
+    if (NULL == pstJsonItem)
+    {
+        json_debug("Key %s is not found in json data.", szKey);
+        return JSON_NOT_FOUND;
+    }
+
+    *piValue = (int)pstJsonItem->unData.lValue;
+
+    return JSON_SUCCESS;
+}
+
+int vtoy_json_get_uint
+(
+    VTOY_JSON *pstJson, 
+    const char *szKey, 
+    grub_uint32_t *puiValue
+)
+{
+    VTOY_JSON *pstJsonItem = NULL;
+    
+    pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey);
+    if (NULL == pstJsonItem)
+    {
+        json_debug("Key %s is not found in json data.", szKey);
+        return JSON_NOT_FOUND;
+    }
+
+    *puiValue = (grub_uint32_t)pstJsonItem->unData.lValue;
+
+    return JSON_SUCCESS;
+}
+
+int vtoy_json_get_uint64
+(
+    VTOY_JSON *pstJson, 
+    const char *szKey, 
+    grub_uint64_t *pui64Value
+)
+{
+    VTOY_JSON *pstJsonItem = NULL;
+    
+    pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_NUMBER, szKey);
+    if (NULL == pstJsonItem)
+    {
+        json_debug("Key %s is not found in json data.", szKey);
+        return JSON_NOT_FOUND;
+    }
+
+    *pui64Value = (grub_uint64_t)pstJsonItem->unData.lValue;
+
+    return JSON_SUCCESS;
+}
+
+int vtoy_json_get_bool
+(
+    VTOY_JSON *pstJson,
+    const char *szKey, 
+    grub_uint8_t *pbValue
+)
+{
+    VTOY_JSON *pstJsonItem = NULL;
+    
+    pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_BOOL, szKey);
+    if (NULL == pstJsonItem)
+    {
+        json_debug("Key %s is not found in json data.", szKey);
+        return JSON_NOT_FOUND;
+    }
+
+    *pbValue = pstJsonItem->unData.lValue > 0 ? 1 : 0;
+
+    return JSON_SUCCESS;
+}
+
+int vtoy_json_get_string
+(
+     VTOY_JSON *pstJson, 
+     const char *szKey, 
+     grub_uint32_t  uiBufLen,
+     char *pcBuf
+)
+{
+    VTOY_JSON *pstJsonItem = NULL;
+    
+    pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_STRING, szKey);
+    if (NULL == pstJsonItem)
+    {
+        json_debug("Key %s is not found in json data.", szKey);
+        return JSON_NOT_FOUND;
+    }
+
+    grub_strncpy(pcBuf, pstJsonItem->unData.pcStrVal, uiBufLen);
+
+    return JSON_SUCCESS;
+}
+
+const char * vtoy_json_get_string_ex(VTOY_JSON *pstJson,  const char *szKey)
+{
+    VTOY_JSON *pstJsonItem = NULL;
+
+    if ((NULL == pstJson) || (NULL == szKey))
+    {
+        return NULL;
+    }
+
+    pstJsonItem = vtoy_json_find_item(pstJson, JSON_TYPE_STRING, szKey);
+    if (NULL == pstJsonItem)
+    {
+        json_debug("Key %s is not found in json data.", szKey);
+        return NULL;
+    }
+
+    return pstJsonItem->unData.pcStrVal;
+}
+
+int vtoy_json_destroy(VTOY_JSON *pstJson)
+{
+    if (NULL == pstJson)
+    {   
+        return JSON_SUCCESS;
+    }
+
+    if (NULL != pstJson->pstChild)
+    {
+        vtoy_json_free(pstJson->pstChild);
+    }
+
+    if (NULL != pstJson->pstNext)
+    {
+        vtoy_json_free(pstJson->pstNext);
+    }
+
+    grub_free(pstJson);
+    
+    return JSON_SUCCESS;
+}
+
diff --git a/GRUB2/grub-2.04/grub-core/ventoy/ventoy_linux.c b/GRUB2/grub-2.04/grub-core/ventoy/ventoy_linux.c
new file mode 100644 (file)
index 0000000..cc35a84
--- /dev/null
@@ -0,0 +1,1050 @@
+/******************************************************************************
+ * ventoy_linux.c 
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/disk.h>
+#include <grub/device.h>
+#include <grub/term.h>
+#include <grub/partition.h>
+#include <grub/file.h>
+#include <grub/normal.h>
+#include <grub/extcmd.h>
+#include <grub/datetime.h>
+#include <grub/i18n.h>
+#include <grub/net.h>
+#include <grub/time.h>
+#include <grub/ventoy.h>
+#include "ventoy_def.h"
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+
+static char * ventoy_get_line(char *start)
+{
+    if (start == NULL)
+    {
+        return NULL;
+    }
+
+    while (*start && *start != '\n')
+    {
+        start++;
+    }
+
+    if (*start == 0)
+    {
+        return NULL;
+    }
+    else
+    {
+        *start = 0;
+        return start + 1;
+    }
+}
+
+static initrd_info * ventoy_find_initrd_by_name(initrd_info *list, const char *name)
+{
+    initrd_info *node = list;
+
+    while (node)
+    {
+        if (grub_strcmp(node->name, name) == 0)
+        {
+            return node;
+        }
+        node = node->next;
+    }
+
+    return NULL;
+}
+
+grub_err_t ventoy_cmd_clear_initrd_list(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    initrd_info *node = g_initrd_img_list;
+    initrd_info *next;
+    
+    (void)ctxt;
+    (void)argc;
+    (void)args;
+
+    while (node)
+    {
+        next = node->next;
+        grub_free(node);
+        node = next;
+    }
+
+    g_initrd_img_list = NULL;
+    g_initrd_img_tail = NULL;
+    g_initrd_img_count = 0;
+    g_valid_initrd_count = 0;
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+grub_err_t ventoy_cmd_dump_initrd_list(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    int i = 0;
+    initrd_info *node = g_initrd_img_list;
+    
+    (void)ctxt;
+    (void)argc;
+    (void)args;
+
+    grub_printf("###################\n");
+    grub_printf("initrd info list: valid count:%d\n", g_valid_initrd_count);
+
+    while (node)
+    {
+        grub_printf("%s ", node->size > 0 ? "*" : " ");
+        grub_printf("%02u %s  offset:%llu  size:%llu \n", i++, node->name, (unsigned long long)node->offset, 
+                    (unsigned long long)node->size);
+        node = node->next;
+    }
+
+    grub_printf("###################\n");
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+static void ventoy_parse_directory(char *path, char *dir, int buflen)
+{   
+    int end;
+    char *pos;
+
+    pos = grub_strstr(path, ")");
+    if (!pos)
+    {
+        pos = path;
+    }
+
+    end = grub_snprintf(dir, buflen, "%s", pos + 1);
+    while (end > 0)
+    {
+        if (dir[end] == '/')
+        {
+            dir[end + 1] = 0;
+            break;
+        }
+        end--;
+    }
+}
+
+static grub_err_t ventoy_isolinux_initrd_collect(grub_file_t file, const char *prefix)
+{
+    int i = 0;
+    int offset;
+    int prefixlen = 0;
+    char *buf = NULL;
+    char *pos = NULL;
+    char *start = NULL;
+    char *nextline = NULL;
+    initrd_info *img = NULL;
+
+    prefixlen = grub_strlen(prefix);
+
+    buf = grub_zalloc(file->size + 2);
+    if (!buf)
+    {
+        return 0;
+    }
+
+    grub_file_read(file, buf, file->size);
+
+    for (start = buf; start; start = nextline)
+    {
+        nextline = ventoy_get_line(start);
+
+        while (ventoy_isspace(*start))
+        {
+            start++;
+        }
+
+        offset = 7; // strlen("initrd=") or "INITRD " or "initrd "
+        pos = grub_strstr(start, "initrd=");
+        if (pos == NULL)
+        {
+            pos = start;
+
+            if (grub_strncmp(start, "INITRD", 6) != 0 && grub_strncmp(start, "initrd", 6) != 0)
+            {
+                if (grub_strstr(start, "xen") &&
+                    ((pos = grub_strstr(start, "--- /install.img")) != NULL ||
+                     (pos = grub_strstr(start, "--- initrd.img")) != NULL
+                    ))
+                {
+                    offset = 4; // "--- "
+                }
+                else
+                {
+                    continue;
+                }
+            }
+        }
+
+        pos += offset; 
+
+        while (1)
+        {
+            i = 0;
+            img = grub_zalloc(sizeof(initrd_info));
+            if (!img)
+            {
+                break;
+            }
+
+            if (*pos != '/')
+            {
+                grub_strcpy(img->name, prefix);
+                i = prefixlen;
+            }
+
+            while (i < 255 && (0 == ventoy_is_word_end(*pos)))
+            {
+                img->name[i++] = *pos++;
+            }
+
+            if (ventoy_find_initrd_by_name(g_initrd_img_list, img->name))
+            {
+                grub_free(img);
+            }
+            else
+            {
+                if (g_initrd_img_list)
+                {
+                    img->prev = g_initrd_img_tail;
+                    g_initrd_img_tail->next = img;
+                }
+                else
+                {
+                    g_initrd_img_list = img;
+                }
+
+                g_initrd_img_tail = img;
+                g_initrd_img_count++;
+            }
+
+            if (*pos == ',')
+            {
+                pos++;
+            }
+            else
+            {
+                break;
+            }
+        }
+    }
+
+    grub_free(buf);
+    return GRUB_ERR_NONE;
+}
+
+static int ventoy_isolinux_initrd_hook(const char *filename, const struct grub_dirhook_info *info, void *data)
+{
+    grub_file_t file = NULL;
+    ventoy_initrd_ctx *ctx = (ventoy_initrd_ctx *)data;
+
+    (void)info;
+
+    if (NULL == grub_strstr(filename, ".cfg") && NULL == grub_strstr(filename, ".CFG"))
+    {
+        return 0;
+    }
+
+    debug("init hook dir <%s%s>\n", ctx->path_prefix, filename);
+
+    file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s%s", ctx->path_prefix, filename);
+    if (!file)
+    {
+        return 0;
+    }
+
+    ventoy_isolinux_initrd_collect(file, ctx->dir_prefix);
+    grub_file_close(file);
+
+    return 0;
+}
+
+grub_err_t ventoy_cmd_isolinux_initrd_collect(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    grub_fs_t fs;
+    grub_device_t dev = NULL;
+    char *device_name = NULL;
+    ventoy_initrd_ctx ctx;
+    char directory[256];
+    
+    (void)ctxt;
+    (void)argc;
+
+    device_name = grub_file_get_device_name(args[0]);
+    if (!device_name)
+    {
+        goto end;
+    }
+
+    dev = grub_device_open(device_name);
+    if (!dev)
+    {
+        goto end;        
+    }
+
+    fs = grub_fs_probe(dev);
+    if (!fs)
+    {
+        goto end;
+    }
+
+    debug("isolinux initrd collect %s\n", args[0]);
+
+    ventoy_parse_directory(args[0], directory, sizeof(directory) - 1);
+    ctx.path_prefix = args[0];
+    ctx.dir_prefix = (argc > 1) ? args[1] : directory;
+
+    debug("path_prefix=<%s> dir_prefix=<%s>\n", ctx.path_prefix, ctx.dir_prefix);
+
+    fs->fs_dir(dev, directory, ventoy_isolinux_initrd_hook, &ctx);
+
+end:
+    check_free(device_name, grub_free);
+    check_free(dev, grub_device_close);
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+static grub_err_t ventoy_grub_cfg_initrd_collect(const char *fileName)
+{
+    int i = 0;
+    grub_file_t file = NULL;
+    char *buf = NULL;
+    char *start = NULL;
+    char *nextline = NULL;
+    initrd_info *img = NULL;
+
+    debug("grub initrd collect %s\n", fileName);
+
+    file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", fileName);
+    if (!file)
+    {
+        return 0;
+    }
+    
+    buf = grub_zalloc(file->size + 2);
+    if (!buf)
+    {
+        grub_file_close(file);
+        return 0;
+    }
+
+    grub_file_read(file, buf, file->size);
+
+    for (start = buf; start; start = nextline)
+    {
+        nextline = ventoy_get_line(start);
+
+        while (ventoy_isspace(*start))
+        {
+            start++;
+        }
+
+        if (grub_strncmp(start, "initrd", 6) != 0)
+        {
+            continue;
+        }
+
+        start += 6;
+        while (*start && (!ventoy_isspace(*start)))
+        {
+            start++;
+        }
+
+        while (ventoy_isspace(*start))
+        {
+            start++;
+        }
+
+        while (*start)
+        {
+            img = grub_zalloc(sizeof(initrd_info));
+            if (!img)
+            {
+                break;
+            }
+            
+            for (i = 0; i < 255 && (0 == ventoy_is_word_end(*start)); i++)
+            {
+                img->name[i] = *start++;
+            }
+
+            if (ventoy_find_initrd_by_name(g_initrd_img_list, img->name))
+            {
+                grub_free(img);
+            }
+            else
+            {
+                if (g_initrd_img_list)
+                {
+                    img->prev = g_initrd_img_tail;
+                    g_initrd_img_tail->next = img;
+                }
+                else
+                {
+                    g_initrd_img_list = img;
+                }
+
+                g_initrd_img_tail = img;
+                g_initrd_img_count++;
+            }
+
+            if (*start == ' ' || *start == '\t')
+            {
+                while (ventoy_isspace(*start))
+                {
+                    start++;
+                }
+            }
+            else
+            {
+                break;
+            }
+        }
+    }
+
+    grub_free(buf);
+    grub_file_close(file);
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+static int ventoy_grub_initrd_hook(const char *filename, const struct grub_dirhook_info *info, void *data)
+{
+    char filePath[256];
+    ventoy_initrd_ctx *ctx = (ventoy_initrd_ctx *)data;
+
+    (void)info;
+
+    debug("ventoy_grub_initrd_hook %s\n", filename);
+
+    if (NULL == grub_strstr(filename, ".cfg") && 
+        NULL == grub_strstr(filename, ".CFG") && 
+        NULL == grub_strstr(filename, ".conf"))
+    {
+        return 0;
+    }
+
+    debug("init hook dir <%s%s>\n", ctx->path_prefix, filename);
+
+    grub_snprintf(filePath, sizeof(filePath) - 1, "%s%s", ctx->dir_prefix, filename);
+    ventoy_grub_cfg_initrd_collect(filePath);
+
+    return 0;
+}
+
+grub_err_t ventoy_cmd_grub_initrd_collect(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    grub_fs_t fs;
+    grub_device_t dev = NULL;
+    char *device_name = NULL;
+    ventoy_initrd_ctx ctx;
+    
+    (void)ctxt;
+    (void)argc;
+
+    if (argc != 2)
+    {
+        return 0;
+    }
+
+    debug("grub initrd collect %s %s\n", args[0], args[1]);
+
+    if (grub_strcmp(args[0], "file") == 0)
+    {
+        return ventoy_grub_cfg_initrd_collect(args[1]);
+    }
+
+    device_name = grub_file_get_device_name(args[1]);
+    if (!device_name)
+    {
+        debug("failed to get device name %s\n", args[1]);
+        goto end;
+    }
+
+    dev = grub_device_open(device_name);
+    if (!dev)
+    {
+        debug("failed to open device %s\n", device_name);
+        goto end;        
+    }
+
+    fs = grub_fs_probe(dev);
+    if (!fs)
+    {
+        debug("failed to probe fs %d\n", grub_errno);
+        goto end;
+    }
+
+    ctx.dir_prefix = args[1];
+    ctx.path_prefix = grub_strstr(args[1], device_name);
+    if (ctx.path_prefix)
+    {
+        ctx.path_prefix += grub_strlen(device_name) + 1;
+    }
+    else
+    {
+        ctx.path_prefix = args[1];
+    }
+
+    debug("ctx.path_prefix:<%s>\n", ctx.path_prefix);
+
+    fs->fs_dir(dev, ctx.path_prefix, ventoy_grub_initrd_hook, &ctx);
+
+end:
+    check_free(device_name, grub_free);
+    check_free(dev, grub_device_close);
+
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+grub_err_t ventoy_cmd_specify_initrd_file(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    initrd_info *img = NULL;
+
+    (void)ctxt;
+    (void)argc;
+
+    debug("ventoy_cmd_specify_initrd_file %s\n", args[0]);
+
+    img = grub_zalloc(sizeof(initrd_info));
+    if (!img)
+    {
+        return 1;
+    }
+
+    grub_strncpy(img->name, args[0], sizeof(img->name));
+    if (ventoy_find_initrd_by_name(g_initrd_img_list, img->name))
+    {
+        grub_free(img);
+    }
+    else
+    {
+        if (g_initrd_img_list)
+        {
+            img->prev = g_initrd_img_tail;
+            g_initrd_img_tail->next = img;
+        }
+        else
+        {
+            g_initrd_img_list = img;
+        }
+
+        g_initrd_img_tail = img;
+        g_initrd_img_count++;
+    }
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+static void ventoy_cpio_newc_fill_int(grub_uint32_t value, char *buf, int buflen)
+{
+    int i;
+    int len;
+    char intbuf[32];
+
+    len = grub_snprintf(intbuf, sizeof(intbuf), "%x", value);
+
+    for (i = 0; i < buflen; i++)
+    {
+        buf[i] = '0';
+    }
+
+    if (len > buflen)
+    {
+        grub_printf("int buf len overflow %d %d\n", len, buflen);
+    }
+    else
+    {
+        grub_memcpy(buf + buflen - len, intbuf, len);    
+    }
+}
+
+int ventoy_cpio_newc_fill_head(void *buf, int filesize, void *filedata, const char *name)
+{
+    int namelen = 0;
+    int headlen = 0;
+    static grub_uint32_t cpio_ino = 0xFFFFFFF0;
+    cpio_newc_header *cpio = (cpio_newc_header *)buf;
+    
+    namelen = grub_strlen(name) + 1;
+    headlen = sizeof(cpio_newc_header) + namelen;
+    headlen = ventoy_align(headlen, 4);
+
+    grub_memset(cpio, '0', sizeof(cpio_newc_header));
+    grub_memset(cpio + 1, 0, headlen - sizeof(cpio_newc_header));
+
+    grub_memcpy(cpio->c_magic, "070701", 6);
+    ventoy_cpio_newc_fill_int(cpio_ino--, cpio->c_ino, 8);
+    ventoy_cpio_newc_fill_int(0100777, cpio->c_mode, 8);
+    ventoy_cpio_newc_fill_int(1, cpio->c_nlink, 8);
+    ventoy_cpio_newc_fill_int(filesize, cpio->c_filesize, 8);
+    ventoy_cpio_newc_fill_int(namelen, cpio->c_namesize, 8);
+    grub_memcpy(cpio + 1, name, namelen);
+
+    if (filedata)
+    {
+        grub_memcpy((char *)cpio + headlen, filedata, filesize);
+    }
+
+    return headlen;
+}
+
+static grub_uint32_t ventoy_linux_get_virt_chunk_size(void)
+{
+    return (sizeof(ventoy_virt_chunk) + g_ventoy_cpio_size) * g_valid_initrd_count;
+}
+
+static void ventoy_linux_fill_virt_data(    grub_uint64_t isosize, ventoy_chain_head *chain)
+{
+    int id = 0;
+    initrd_info *node;
+    grub_uint64_t sector;
+    grub_uint32_t offset;
+    grub_uint32_t cpio_secs;
+    grub_uint32_t initrd_secs;
+    char *override;
+    ventoy_virt_chunk *cur;
+    char name[32];
+
+    override = (char *)chain + chain->virt_chunk_offset;
+    sector = (isosize + 2047) / 2048;
+    cpio_secs = g_ventoy_cpio_size / 2048;
+
+    offset = g_valid_initrd_count * sizeof(ventoy_virt_chunk);
+    cur = (ventoy_virt_chunk *)override;
+
+    for (node = g_initrd_img_list; node; node = node->next)
+    {
+        if (node->size == 0)
+        {
+            continue;
+        }
+
+        initrd_secs = (grub_uint32_t)((node->size + 2047) / 2048);
+
+        cur->mem_sector_start   = sector;
+        cur->mem_sector_end     = cur->mem_sector_start + cpio_secs;
+        cur->mem_sector_offset  = offset;
+        cur->remap_sector_start = cur->mem_sector_end;
+        cur->remap_sector_end   = cur->remap_sector_start + initrd_secs;
+        cur->org_sector_start   = (grub_uint32_t)(node->offset / 2048);
+
+        grub_memcpy(g_ventoy_runtime_buf, &chain->os_param, sizeof(ventoy_os_param));
+
+        grub_memset(name, 0, 16);
+        grub_snprintf(name, sizeof(name), "initrd%03d", ++id);
+
+        grub_memcpy(g_ventoy_initrd_head + 1, name, 16);
+        ventoy_cpio_newc_fill_int((grub_uint32_t)node->size, g_ventoy_initrd_head->c_filesize, 8);
+
+        grub_memcpy(override + offset, g_ventoy_cpio_buf, g_ventoy_cpio_size);
+
+        chain->virt_img_size_in_bytes += g_ventoy_cpio_size + initrd_secs * 2048;
+
+        offset += g_ventoy_cpio_size;
+        sector += cpio_secs + initrd_secs;
+        cur++;
+    }
+
+    return;
+}
+
+static grub_uint32_t ventoy_linux_get_override_chunk_size(void)
+{
+    return sizeof(ventoy_override_chunk) * g_valid_initrd_count;
+}
+
+static void ventoy_linux_fill_override_data(    grub_uint64_t isosize, void *override)
+{
+    initrd_info *node;
+    grub_uint64_t sector;
+    ventoy_override_chunk *cur;
+
+    sector = (isosize + 2047) / 2048;
+
+    cur = (ventoy_override_chunk *)override;
+    for (node = g_initrd_img_list; node; node = node->next)
+    {
+        if (node->size == 0)
+        {
+            continue;
+        }
+
+        if (node->iso_type == 0)
+        {
+            ventoy_iso9660_override *dirent = (ventoy_iso9660_override *)node->override_data;
+
+            node->override_length   = sizeof(ventoy_iso9660_override);
+            dirent->first_sector    = (grub_uint32_t)sector;
+            dirent->size            = (grub_uint32_t)(node->size + g_ventoy_cpio_size);
+            dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector);
+            dirent->size_be         = grub_swap_bytes32(dirent->size);
+
+            sector += (dirent->size + 2047) / 2048;
+        }
+        else
+        {
+            ventoy_udf_override *udf = (ventoy_udf_override *)node->override_data;
+            
+            node->override_length = sizeof(ventoy_udf_override);
+            udf->length   = (grub_uint32_t)(node->size + g_ventoy_cpio_size);
+            udf->position = (grub_uint32_t)sector - node->udf_start_block;
+
+            sector += (udf->length + 2047) / 2048;
+        }
+
+        cur->img_offset = node->override_offset;
+        cur->override_size = node->override_length;
+        grub_memcpy(cur->override_data, node->override_data, cur->override_size);
+        cur++;
+    }
+
+    return;
+}
+
+grub_err_t ventoy_cmd_initrd_count(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    char buf[32] = {0};
+    
+    (void)ctxt;
+    (void)argc;
+    (void)args;
+
+    if (argc == 1)
+    {
+        grub_snprintf(buf, sizeof(buf), "%d", g_valid_initrd_count);
+        grub_env_set(args[0], buf);
+    }
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+static grub_err_t ventoy_linux_locate_initrd(int filt, int *filtcnt)
+{
+    int data;
+    int sizefilt = 0;
+    grub_file_t file;
+    initrd_info *node;
+
+    debug("ventoy_linux_locate_initrd %d\n", filt);
+
+    g_valid_initrd_count = 0;
+    
+    for (node = g_initrd_img_list; node; node = node->next)
+    {
+        file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "(loop)%s", node->name);
+        if (!file)
+        {
+            continue;
+        }
+
+        debug("file <%s> size:%d\n", node->name, (int)file->size);
+
+        /* initrd file too small */
+        if (filt > 0 && file->size <= g_ventoy_cpio_size + 2048)
+        {
+            debug("file size too small %d\n", (int)g_ventoy_cpio_size);
+            grub_file_close(file);
+            sizefilt++;
+            continue;
+        }
+
+        if (grub_strcmp(file->fs->name, "iso9660") == 0)
+        {
+            node->iso_type = 0;
+            node->override_offset = grub_iso9660_get_last_file_dirent_pos(file) + 2;
+            
+            grub_file_read(file, &data, 1); // just read for hook trigger
+            node->offset = grub_iso9660_get_last_read_pos(file);
+        }
+        else
+        {
+            /* TBD */
+        }
+
+        node->size = file->size;
+        g_valid_initrd_count++;
+
+        grub_file_close(file);
+    }
+
+    *filtcnt = sizefilt;
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+
+grub_err_t ventoy_cmd_linux_locate_initrd(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    int sizefilt = 0;
+
+    (void)ctxt;
+    (void)argc;
+    (void)args;
+
+    ventoy_linux_locate_initrd(1, &sizefilt);
+
+    if (g_valid_initrd_count == 0 && sizefilt > 0)
+    {
+        ventoy_linux_locate_initrd(0, &sizefilt);
+    }
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+grub_err_t ventoy_cmd_load_cpio(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    grub_uint8_t *buf;
+    grub_uint32_t mod;
+    grub_uint32_t headlen;
+    grub_uint32_t initrd_head_len;
+    grub_uint32_t padlen;
+    grub_uint32_t img_chunk_size;
+    grub_file_t file;
+        
+    (void)ctxt;
+    (void)argc;
+
+    if (argc != 1)
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "Usage: %s cpiofile\n", cmd_raw_name); 
+    }
+
+    if (g_img_chunk_list.chunk == NULL || g_img_chunk_list.cur_chunk == 0)
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "image chunk is null\n");
+    }
+
+    img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
+
+    file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
+    if (!file)
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't open file %s\n", args[0]); 
+    }
+
+    if (g_ventoy_cpio_buf)
+    {
+        grub_free(g_ventoy_cpio_buf);
+        g_ventoy_cpio_buf = NULL;
+        g_ventoy_cpio_size = 0;
+    }
+
+    g_ventoy_cpio_buf = grub_malloc(file->size + 4096 + img_chunk_size);
+    if (NULL == g_ventoy_cpio_buf)
+    {
+        grub_file_close(file);
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't alloc memory %llu\n", file->size + 4096 + img_chunk_size); 
+    }
+
+    grub_file_read(file, g_ventoy_cpio_buf, file->size);
+
+    buf = (grub_uint8_t *)(g_ventoy_cpio_buf + file->size - 4);
+    while (*((grub_uint32_t *)buf) != 0x37303730)
+    {
+        buf -= 4;
+    }
+
+    /* get initrd head len */
+    initrd_head_len = ventoy_cpio_newc_fill_head(buf, 0, NULL, "initrd000.xx");
+
+    /* step1: insert image chunk data to cpio */
+    headlen = ventoy_cpio_newc_fill_head(buf, img_chunk_size, g_img_chunk_list.chunk, "ventoy/ventoy_image_map");
+    buf += headlen + ventoy_align(img_chunk_size, 4);
+
+    /* step2: insert os param to cpio */
+    headlen = ventoy_cpio_newc_fill_head(buf, 0, NULL, "ventoy/ventoy_os_param");
+    padlen = sizeof(ventoy_os_param);
+    g_ventoy_cpio_size = (grub_uint32_t)(buf - g_ventoy_cpio_buf) + headlen + padlen + initrd_head_len;
+    mod = g_ventoy_cpio_size % 2048;
+    if (mod)
+    {
+        g_ventoy_cpio_size += 2048 - mod;
+        padlen += 2048 - mod;
+    }
+
+    /* update os param data size, the data will be updated before chain boot */
+    ventoy_cpio_newc_fill_int(padlen, ((cpio_newc_header *)buf)->c_filesize, 8);
+    g_ventoy_runtime_buf = (grub_uint8_t *)buf + headlen;
+
+    /* step3: fill initrd cpio head, the file size will be updated before chain boot */
+    g_ventoy_initrd_head = (cpio_newc_header *)(g_ventoy_runtime_buf + padlen);
+    ventoy_cpio_newc_fill_head(g_ventoy_initrd_head, 0, NULL, "initrd000.xx");
+
+    grub_file_close(file);
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+
+grub_err_t ventoy_cmd_linux_chain_data(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    int ventoy_compatible = 0;
+    grub_uint32_t size = 0;
+    grub_uint64_t isosize = 0;
+    grub_uint32_t boot_catlog = 0;
+    grub_uint32_t img_chunk_size = 0;
+    grub_uint32_t override_size = 0;
+    grub_uint32_t virt_chunk_size = 0;
+    grub_file_t file;
+    grub_disk_t disk;
+    const char *pLastChain = NULL;
+    const char *compatible;
+    ventoy_chain_head *chain;
+    char envbuf[64];
+    
+    (void)ctxt;
+    (void)argc;
+
+    compatible = grub_env_get("ventoy_compatible");
+    if (compatible && compatible[0] == 'Y')
+    {
+        ventoy_compatible = 1;
+    }
+
+    if ((NULL == g_img_chunk_list.chunk) || (0 == ventoy_compatible && g_ventoy_cpio_buf == NULL))
+    {
+        grub_printf("ventoy not ready\n");
+        return 1;
+    }
+
+    file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
+    if (!file)
+    {
+        return 1;
+    }
+
+    isosize = file->size;
+
+    boot_catlog = ventoy_get_iso_boot_catlog(file);
+    if (boot_catlog)
+    {
+        if (ventoy_is_efi_os() && (!ventoy_has_efi_eltorito(file, boot_catlog)))
+        {
+            grub_env_set("LoadIsoEfiDriver", "on");
+        }
+    }
+    else
+    {
+        if (ventoy_is_efi_os())
+        {
+            grub_env_set("LoadIsoEfiDriver", "on");
+        }
+        else
+        {
+            return grub_error(GRUB_ERR_BAD_ARGUMENT, "File %s is not bootable", args[0]);
+        }
+    }
+    
+    img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
+    
+    if (ventoy_compatible)
+    {
+        size = sizeof(ventoy_chain_head) + img_chunk_size;
+    }
+    else
+    {
+        override_size = ventoy_linux_get_override_chunk_size();
+        virt_chunk_size = ventoy_linux_get_virt_chunk_size();
+        size = sizeof(ventoy_chain_head) + img_chunk_size + override_size + virt_chunk_size;
+    }
+    
+    pLastChain = grub_env_get("vtoy_chain_mem_addr");
+    if (pLastChain)
+    {
+        chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16);
+        if (chain)
+        {
+            debug("free last chain memory %p\n", chain);
+            grub_free(chain);
+        }
+    }
+
+    chain = grub_malloc(size);
+    if (!chain)
+    {
+        grub_printf("Failed to alloc chain memory size %u\n", size);
+        grub_file_close(file);
+        return 1;
+    }
+
+    grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)chain);
+    grub_env_set("vtoy_chain_mem_addr", envbuf);
+    grub_snprintf(envbuf, sizeof(envbuf), "%u", size);
+    grub_env_set("vtoy_chain_mem_size", envbuf);
+
+    grub_memset(chain, 0, sizeof(ventoy_chain_head));
+
+    /* part 1: os parameter */
+    ventoy_fill_os_param(file, &(chain->os_param));
+
+    /* part 2: chain head */
+    disk = file->device->disk;
+    chain->disk_drive = disk->id;
+    chain->disk_sector_size = (1 << disk->log_sector_size);
+    chain->real_img_size_in_bytes = file->size;
+    chain->virt_img_size_in_bytes = (file->size + 2047) / 2048 * 2048;
+    chain->boot_catalog = boot_catlog;
+
+    if (!ventoy_is_efi_os())
+    {
+        grub_file_seek(file, boot_catlog * 2048);
+        grub_file_read(file, chain->boot_catalog_sector, sizeof(chain->boot_catalog_sector));
+    }
+
+    /* part 3: image chunk */
+    chain->img_chunk_offset = sizeof(ventoy_chain_head);
+    chain->img_chunk_num = g_img_chunk_list.cur_chunk;
+    grub_memcpy((char *)chain + chain->img_chunk_offset, g_img_chunk_list.chunk, img_chunk_size);
+
+    if (ventoy_compatible)
+    {
+        return 0;
+    }
+
+    if (g_valid_initrd_count == 0)
+    {
+        return 0;
+    }
+
+    /* part 4: override chunk */
+    chain->override_chunk_offset = chain->img_chunk_offset + img_chunk_size;
+    chain->override_chunk_num = g_valid_initrd_count;
+    ventoy_linux_fill_override_data(isosize, (char *)chain + chain->override_chunk_offset);
+
+    /* part 5: virt chunk */
+    chain->virt_chunk_offset = chain->override_chunk_offset + override_size;
+    chain->virt_chunk_num = g_valid_initrd_count;
+    ventoy_linux_fill_virt_data(isosize, chain);
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
diff --git a/GRUB2/grub-2.04/grub-core/ventoy/ventoy_plugin.c b/GRUB2/grub-2.04/grub-core/ventoy/ventoy_plugin.c
new file mode 100644 (file)
index 0000000..cd1a1d8
--- /dev/null
@@ -0,0 +1,151 @@
+/******************************************************************************
+ * ventoy_plugin.c 
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/disk.h>
+#include <grub/device.h>
+#include <grub/term.h>
+#include <grub/partition.h>
+#include <grub/file.h>
+#include <grub/normal.h>
+#include <grub/extcmd.h>
+#include <grub/datetime.h>
+#include <grub/i18n.h>
+#include <grub/net.h>
+#include <grub/time.h>
+#include <grub/ventoy.h>
+#include "ventoy_def.h"
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+static int ventoy_plugin_theme_entry(VTOY_JSON *json, const char *isodisk)
+{
+    const char *value;
+    char filepath[256];
+    
+    value = vtoy_json_get_string_ex(json->pstChild, "file");
+    if (value)
+    {
+        grub_snprintf(filepath, sizeof(filepath), "%s/ventoy/%s", isodisk, value);
+        if (ventoy_is_file_exist(filepath) == 0)
+        {
+            debug("Theme file %s does not exist\n", filepath);
+            return 0;
+        }
+
+        debug("vtoy_theme %s\n", filepath);
+        grub_env_set("vtoy_theme", filepath);
+    }
+    
+    value = vtoy_json_get_string_ex(json->pstChild, "gfxmode");
+    if (value)
+    {
+        debug("vtoy_gfxmode %s\n", value);
+        grub_env_set("vtoy_gfxmode", value);
+    }
+
+    return 0;
+}
+
+static plugin_entry g_plugin_entries[] = 
+{
+    { "theme", ventoy_plugin_theme_entry },
+};
+
+static int ventoy_parse_plugin_config(VTOY_JSON *json, const char *isodisk)
+{
+    int i;
+    VTOY_JSON *cur = json;
+
+    while (cur)
+    {
+        for (i = 0; i < (int)ARRAY_SIZE(g_plugin_entries); i++)
+        {
+            if (grub_strcmp(g_plugin_entries[i].key, cur->pcName) == 0)
+            {
+                debug("Plugin entry for %s\n", g_plugin_entries[i].key);
+                g_plugin_entries[i].entryfunc(cur, isodisk);
+                break;
+            }
+        }
+    
+        cur = cur->pstNext;
+    }
+
+    return 0;
+}
+
+grub_err_t ventoy_cmd_load_plugin(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    int ret = 0;
+    char *buf = NULL;
+    grub_file_t file;
+    VTOY_JSON *json = NULL;
+    
+    (void)ctxt;
+    (void)argc;
+
+    file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s/ventoy/ventoy.json", args[0]);
+    if (!file)
+    {
+        return GRUB_ERR_NONE;
+    }
+
+    debug("json configuration file size %d\n", (int)file->size);
+    
+    buf = grub_malloc(file->size + 1);
+    if (!buf)
+    {
+        grub_file_close(file);
+        return 1;
+    }
+    
+    buf[file->size] = 0;
+    grub_file_read(file, buf, file->size);
+    grub_file_close(file);
+
+    json = vtoy_json_create();
+    if (!json)
+    {
+        return 1;
+    }
+
+    
+
+    ret = vtoy_json_parse(json, buf);
+    if (ret)
+    {
+        debug("Failed to parse json string %d\n", ret);
+        grub_free(buf);
+        return 1;
+    }
+
+    ventoy_parse_plugin_config(json->pstChild, args[0]);
+
+    vtoy_json_destroy(json);
+
+    grub_free(buf);
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
diff --git a/GRUB2/grub-2.04/grub-core/ventoy/ventoy_windows.c b/GRUB2/grub-2.04/grub-core/ventoy/ventoy_windows.c
new file mode 100644 (file)
index 0000000..7ec7b08
--- /dev/null
@@ -0,0 +1,931 @@
+/******************************************************************************
+ * ventoy_windows.c 
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/disk.h>
+#include <grub/device.h>
+#include <grub/term.h>
+#include <grub/partition.h>
+#include <grub/file.h>
+#include <grub/normal.h>
+#include <grub/extcmd.h>
+#include <grub/datetime.h>
+#include <grub/i18n.h>
+#include <grub/net.h>
+#include <grub/time.h>
+#include <grub/crypto.h>
+#include <grub/ventoy.h>
+#include "ventoy_def.h"
+
+GRUB_MOD_LICENSE ("GPLv3+");
+
+wim_hash g_old_hash;
+wim_tail g_wim_data;
+
+static wim_lookup_entry *g_replace_look = NULL;
+
+grub_ssize_t lzx_decompress ( const void *data, grub_size_t len, void *buf );
+
+static int wim_name_cmp(const char *search, grub_uint16_t *name, grub_uint16_t namelen)
+{
+    char c1 = vtoy_to_upper(*search);
+    char c2 = vtoy_to_upper(*name);
+
+    while (namelen > 0 && (c1 == c2))
+    {
+        search++;
+        name++;
+        namelen--;
+        
+        c1 = vtoy_to_upper(*search);
+        c2 = vtoy_to_upper(*name);
+    }
+
+    if (namelen == 0 && *search == 0)
+    {
+        return 0;
+    }
+
+    return 1;
+}
+
+static int ventoy_is_pe64(grub_uint8_t *buffer)
+{
+    grub_uint32_t pe_off;
+
+    if (buffer[0] != 'M' || buffer[1] != 'Z')
+    {
+        return 0;
+    }
+    
+    pe_off = *(grub_uint32_t *)(buffer + 60);
+
+    if (buffer[pe_off] != 'P' || buffer[pe_off + 1] != 'E')
+    {
+        return 0;
+    }
+
+    if (*(grub_uint16_t *)(buffer + pe_off + 24) == 0x020b)
+    {
+        return 1;
+    }
+
+    return 0;
+}
+
+grub_err_t ventoy_cmd_wimdows_reset(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    (void)ctxt;
+    (void)argc;
+    (void)args;
+    
+    check_free(g_wim_data.jump_bin_data, grub_free);
+    check_free(g_wim_data.new_meta_data, grub_free);
+    check_free(g_wim_data.new_lookup_data, grub_free);
+
+    grub_memset(&g_wim_data, 0, sizeof(g_wim_data));
+
+    return 0;
+}
+
+static int ventoy_load_jump_exe(const char *path, grub_uint8_t **data, grub_uint32_t *size, wim_hash *hash)
+{
+    grub_uint32_t i;
+    grub_uint32_t align;
+    grub_file_t file;
+
+    debug("windows load jump %s\n", path);
+    
+    file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", path);
+    if (!file)
+    {
+        debug("Can't open file %s\n", path); 
+        return 1;
+    }
+
+    align = ventoy_align((int)file->size, 2048);
+
+    debug("file %s size:%d align:%u\n", path, (int)file->size, align);
+
+    *size = (grub_uint32_t)file->size;
+    *data = (grub_uint8_t *)grub_malloc(align);
+    if ((*data) == NULL)
+    {
+        debug("Failed to alloc memory size %u\n", align);
+        goto end;
+    }
+
+    grub_file_read(file, (*data), file->size);
+
+    if (hash)
+    {
+        grub_crypto_hash(GRUB_MD_SHA1, hash->sha1, (*data), file->size);
+
+        if (g_ventoy_debug)
+        {
+            debug("%s", "jump bin 64 hash: ");
+            for (i = 0; i < sizeof(hash->sha1); i++)
+            {
+                ventoy_debug("%02x ", hash->sha1[i]);
+            }
+            ventoy_debug("\n");
+        }
+    }
+
+end:
+
+    grub_file_close(file);
+    return 0;
+}
+
+static int ventoy_get_override_info(grub_file_t file)
+{
+    grub_uint32_t start_block;
+    grub_uint64_t file_offset;
+    grub_uint64_t override_offset;
+    grub_uint32_t override_len;
+    grub_uint64_t fe_entry_size_offset;
+    
+    if (grub_strcmp(file->fs->name, "iso9660") == 0)
+    {
+        g_wim_data.iso_type = 0;
+        override_len = sizeof(ventoy_iso9660_override);
+        override_offset = grub_iso9660_get_last_file_dirent_pos(file) + 2;
+
+        grub_file_read(file, &start_block, 1); // just read for hook trigger
+        file_offset = grub_iso9660_get_last_read_pos(file);
+
+        debug("iso9660 wim size:%llu override_offset:%llu file_offset:%llu\n", 
+            (ulonglong)file->size, (ulonglong)override_offset, (ulonglong)file_offset);
+    }
+    else
+    {
+        g_wim_data.iso_type = 1;    
+        override_len = sizeof(ventoy_udf_override);
+        override_offset = grub_udf_get_last_file_attr_offset(file, &start_block, &fe_entry_size_offset);
+        
+        file_offset = grub_udf_get_file_offset(file);
+
+        debug("UDF wim size:%llu override_offset:%llu file_offset:%llu start_block=%u\n", 
+            (ulonglong)file->size, (ulonglong)override_offset, (ulonglong)file_offset, start_block);
+    }
+
+    g_wim_data.file_offset = file_offset;
+    g_wim_data.udf_start_block = start_block;
+    g_wim_data.fe_entry_size_offset = fe_entry_size_offset;
+    g_wim_data.override_offset = override_offset;
+    g_wim_data.override_len = override_len;
+
+    return 0;
+}
+
+static int ventoy_read_resource(grub_file_t fp, wim_resource_header *head, void **buffer)
+{
+    int decompress_len = 0;
+    int total_decompress = 0;
+    grub_uint32_t i = 0;
+    grub_uint32_t chunk_num = 0;
+    grub_uint32_t chunk_size = 0;
+    grub_uint32_t last_chunk_size = 0;
+    grub_uint32_t last_decompress_size = 0;
+    grub_uint32_t cur_offset = 0;
+    grub_uint8_t *cur_dst = NULL;
+    grub_uint8_t *buffer_compress = NULL;
+    grub_uint8_t *buffer_decompress = NULL;
+    grub_uint32_t *chunk_offset = NULL;
+
+    buffer_decompress = (grub_uint8_t *)grub_malloc(head->raw_size + head->size_in_wim);
+    if (NULL == buffer_decompress)
+    {
+        return 0;
+    }
+
+    grub_file_seek(fp, head->offset);
+
+    if (head->size_in_wim == head->raw_size)
+    {
+        grub_file_read(fp, buffer_decompress, head->size_in_wim);
+        *buffer = buffer_decompress;
+        return 0;
+    }
+
+    buffer_compress = buffer_decompress + head->raw_size;
+    grub_file_read(fp, buffer_compress, head->size_in_wim);
+
+    chunk_num = (head->raw_size + WIM_CHUNK_LEN - 1) / WIM_CHUNK_LEN;
+    cur_offset = (chunk_num - 1) * 4;
+    chunk_offset = (grub_uint32_t *)buffer_compress;
+    
+    cur_dst = buffer_decompress;
+    
+    for (i = 0; i < chunk_num - 1; i++)
+    {
+        chunk_size = (i == 0) ? chunk_offset[i] : chunk_offset[i] - chunk_offset[i - 1];
+
+        if (WIM_CHUNK_LEN == chunk_size)
+        {
+            grub_memcpy(cur_dst, buffer_compress + cur_offset, chunk_size);
+            decompress_len = (int)chunk_size; 
+        }
+        else
+        {
+            decompress_len = (int)lzx_decompress(buffer_compress + cur_offset, chunk_size, cur_dst);
+        }
+
+        //debug("chunk_size:%u decompresslen:%d\n", chunk_size, decompress_len);
+        
+        total_decompress += decompress_len;
+        cur_dst += decompress_len;
+        cur_offset += chunk_size;
+    }
+
+    /* last chunk */
+    last_chunk_size = (grub_uint32_t)(head->size_in_wim - cur_offset);
+    last_decompress_size = head->raw_size - total_decompress;
+    
+    if (last_chunk_size < WIM_CHUNK_LEN && last_chunk_size == last_decompress_size)
+    {
+        debug("Last chunk %u uncompressed\n", last_chunk_size);
+        grub_memcpy(cur_dst, buffer_compress + cur_offset, last_chunk_size);
+        decompress_len = (int)last_chunk_size; 
+    }
+    else
+    {
+        decompress_len = (int)lzx_decompress(buffer_compress + cur_offset, head->size_in_wim - cur_offset, cur_dst);            
+    }
+    
+    cur_dst += decompress_len;
+    total_decompress += decompress_len;
+
+    if (cur_dst != buffer_decompress + head->raw_size)
+    {
+        debug("head->size_in_wim:%llu head->raw_size:%llu cur_dst:%p buffer_decompress:%p total_decompress:%d\n", 
+            (ulonglong)head->size_in_wim, (ulonglong)head->raw_size, cur_dst, buffer_decompress, total_decompress);
+        grub_free(buffer_decompress);
+        return 1;
+    }
+    
+    *buffer = buffer_decompress;
+    return 0;
+}
+
+
+static wim_directory_entry * search_wim_dirent(wim_directory_entry *dir, const char *search_name)
+{
+    do 
+    {
+        if (dir->len && dir->name_len)
+        {
+            if (wim_name_cmp(search_name, (grub_uint16_t *)(dir + 1), dir->name_len / 2) == 0)
+            {
+                return dir;
+            }
+        }
+        dir = (wim_directory_entry *)((grub_uint8_t *)dir + dir->len);
+    } while(dir->len);
+        
+    return NULL;
+}
+
+static wim_directory_entry * search_full_wim_dirent
+(
+    void *meta_data, 
+    wim_directory_entry *dir,
+    const char **path    
+)
+{
+    wim_directory_entry *subdir = NULL;
+    wim_directory_entry *search = dir;
+
+    while (*path)
+    {
+        subdir = (wim_directory_entry *)((char *)meta_data + search->subdir);
+        search = search_wim_dirent(subdir, *path);
+        if (!search)
+        {
+            debug("%s search failed\n", *path);
+        }
+
+        path++;
+    }
+    return search;
+}
+
+static wim_directory_entry * search_replace_wim_dirent(void *meta_data, wim_directory_entry *dir)
+{
+    wim_directory_entry *wim_dirent = NULL;
+    const char *winpeshl_path[] = { "Windows", "System32", "winpeshl.exe", NULL };
+    const char *pecmd_path[] = { "Windows", "System32", "PECMD.exe", NULL };
+
+    wim_dirent = search_full_wim_dirent(meta_data, dir, winpeshl_path);
+    if (wim_dirent)
+    {
+        return wim_dirent;
+    }
+    
+    wim_dirent = search_full_wim_dirent(meta_data, dir, pecmd_path);
+    if (wim_dirent)
+    {
+        return wim_dirent;
+    }
+
+    return NULL;
+}
+
+
+static wim_lookup_entry * ventoy_find_look_entry(wim_header *header, wim_lookup_entry *lookup, wim_hash *hash)
+{
+    grub_uint32_t i = 0;
+    
+    for (i = 0; i < (grub_uint32_t)header->lookup.raw_size / sizeof(wim_lookup_entry); i++)
+    {
+        if (grub_memcmp(&lookup[i].hash, hash, sizeof(wim_hash)) == 0)
+        {
+            return lookup + i;
+        }
+    }
+
+    return NULL;
+}
+
+static wim_lookup_entry * ventoy_find_meta_entry(wim_header *header, wim_lookup_entry *lookup)
+{
+    grub_uint32_t i = 0;
+    grub_uint32_t index = 0;;
+
+    if ((header == NULL) || (lookup == NULL))
+    {
+        return NULL;
+    }
+    
+    for (i = 0; i < (grub_uint32_t)header->lookup.raw_size / sizeof(wim_lookup_entry); i++)
+    {
+        if (lookup[i].resource.flags & RESHDR_FLAG_METADATA)
+        {
+            index++;
+            if (index == header->boot_index)
+            {
+                return lookup + i;
+            }
+        }
+    }
+
+    return NULL;
+}
+
+static int ventoy_update_all_hash(void *meta_data, wim_directory_entry *dir)
+{
+    if ((meta_data == NULL) || (dir == NULL))
+    {
+        return 0;
+    }
+
+    if (dir->len == 0)
+    {
+        return 0;
+    }
+
+    do
+    {
+        if (dir->subdir == 0 && grub_memcmp(dir->hash.sha1, g_old_hash.sha1, sizeof(wim_hash)) == 0)
+        {
+            debug("find target file, name_len:%u upadte hash\n", dir->name_len);
+            grub_memcpy(dir->hash.sha1, &(g_wim_data.bin_hash), sizeof(wim_hash));
+        }
+        
+        if (dir->subdir)
+        {
+            ventoy_update_all_hash(meta_data, (wim_directory_entry *)((char *)meta_data + dir->subdir));
+        }
+    
+        dir = (wim_directory_entry *)((char *)dir + dir->len);
+    } while (dir->len);
+
+    return 0;
+}
+
+static int ventoy_cat_exe_file_data(grub_uint32_t exe_len, grub_uint8_t *exe_data)
+{
+    int pe64 = 0;
+    char file[256];
+    grub_uint32_t jump_len = 0;
+    grub_uint32_t jump_align = 0;
+    grub_uint8_t *jump_data = NULL;
+
+    pe64 = ventoy_is_pe64(exe_data);
+    
+    grub_snprintf(file, sizeof(file), "%s/vtoyjump%d.exe", grub_env_get("vtoy_path"), pe64 ? 64 : 32);
+    ventoy_load_jump_exe(file, &jump_data, &jump_len, NULL);
+    jump_align = ventoy_align(jump_len, 16);
+    
+    g_wim_data.jump_exe_len = jump_len;
+    g_wim_data.bin_raw_len = jump_align + sizeof(ventoy_os_param) + exe_len;
+    g_wim_data.bin_align_len = ventoy_align(g_wim_data.bin_raw_len, 2048);
+    
+    g_wim_data.jump_bin_data = grub_malloc(g_wim_data.bin_align_len);
+    if (g_wim_data.jump_bin_data)
+    {
+        grub_memcpy(g_wim_data.jump_bin_data, jump_data, jump_len);
+        grub_memcpy(g_wim_data.jump_bin_data + jump_align + sizeof(ventoy_os_param), exe_data, exe_len);
+    }
+
+    debug("jump_exe_len:%u bin_raw_len:%u bin_align_len:%u\n", 
+        g_wim_data.jump_exe_len, g_wim_data.bin_raw_len, g_wim_data.bin_align_len);
+    
+    return 0;
+}
+
+static int ventoy_update_before_chain(ventoy_os_param *param)
+{
+    grub_uint32_t jump_align = 0;
+    wim_lookup_entry *meta_look = NULL;
+    wim_security_header *security = NULL;
+    wim_directory_entry *rootdir = NULL;
+    wim_header *head = &(g_wim_data.wim_header);    
+    wim_lookup_entry *lookup = (wim_lookup_entry *)g_wim_data.new_lookup_data;
+
+    jump_align = ventoy_align(g_wim_data.jump_exe_len, 16);
+    if (g_wim_data.jump_bin_data)
+    {
+        grub_memcpy(g_wim_data.jump_bin_data + jump_align, param, sizeof(ventoy_os_param));        
+    }
+
+    grub_crypto_hash(GRUB_MD_SHA1, g_wim_data.bin_hash.sha1, g_wim_data.jump_bin_data, g_wim_data.bin_raw_len);
+
+    security = (wim_security_header *)g_wim_data.new_meta_data;
+    rootdir = (wim_directory_entry *)(g_wim_data.new_meta_data + ((security->len + 7) & 0xFFFFFFF8U));
+
+    /* update all winpeshl.exe dirent entry's hash */
+    ventoy_update_all_hash(g_wim_data.new_meta_data, rootdir);
+
+    /* update winpeshl.exe lookup entry data (hash/offset/length) */
+    if (g_replace_look)
+    {
+        debug("update replace lookup entry_id:%ld\n", ((long)g_replace_look - (long)lookup) / sizeof(wim_lookup_entry));
+        g_replace_look->resource.raw_size = g_wim_data.bin_raw_len;
+        g_replace_look->resource.size_in_wim = g_wim_data.bin_raw_len;
+        g_replace_look->resource.flags = 0;
+        g_replace_look->resource.offset = g_wim_data.wim_align_size;
+
+        grub_memcpy(g_replace_look->hash.sha1, g_wim_data.bin_hash.sha1, sizeof(wim_hash));
+    }
+
+    /* update metadata's hash */
+    meta_look = ventoy_find_meta_entry(head, lookup);
+    if (meta_look)
+    {
+        debug("find meta lookup entry_id:%ld\n", ((long)meta_look - (long)lookup) / sizeof(wim_lookup_entry));
+        grub_memcpy(&meta_look->resource, &head->metadata, sizeof(wim_resource_header));
+        grub_crypto_hash(GRUB_MD_SHA1, meta_look->hash.sha1, g_wim_data.new_meta_data, g_wim_data.new_meta_len);
+    }
+
+    return 0;
+}
+
+grub_err_t ventoy_cmd_wimdows_locate_wim(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    int rc;
+    grub_file_t file;
+    grub_uint32_t exe_len;
+    grub_uint8_t *exe_data = NULL;
+    grub_uint8_t *decompress_data = NULL;
+    wim_lookup_entry *lookup = NULL;
+    wim_security_header *security = NULL;
+    wim_directory_entry *rootdir = NULL;
+    wim_directory_entry *search = NULL;
+    wim_header *head = &(g_wim_data.wim_header);    
+    
+    (void)ctxt;
+    (void)argc;
+
+    debug("windows locate wim start %s\n", args[0]);
+    
+    file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
+    if (!file)
+    {
+        return grub_error(GRUB_ERR_BAD_ARGUMENT, "Can't open file %s\n", args[0]); 
+    }
+
+    ventoy_get_override_info(file);
+
+    grub_file_seek(file, 0);
+    grub_file_read(file, head, sizeof(wim_header));
+
+    if (grub_memcmp(head->signature, WIM_HEAD_SIGNATURE, sizeof(head->signature)))
+    {
+        debug("Not a valid wim file %s\n", (char *)head->signature);
+        grub_file_close(file);
+        return 1;
+    }
+
+    if (head->flags & FLAG_HEADER_COMPRESS_XPRESS)
+    {
+        debug("Xpress compress is not supported 0x%x\n", head->flags);
+        grub_file_close(file);
+        return 1;
+    }
+
+    rc = ventoy_read_resource(file, &head->metadata, (void **)&decompress_data);
+    if (rc)
+    {
+        grub_printf("failed to read meta data %d\n", rc);
+        grub_file_close(file);
+        return 1;
+    }
+
+    security = (wim_security_header *)decompress_data;
+    rootdir = (wim_directory_entry *)(decompress_data + ((security->len + 7) & 0xFFFFFFF8U));
+
+    /* search winpeshl.exe dirent entry */
+    search = search_replace_wim_dirent(decompress_data, rootdir);
+    if (!search)
+    {
+        debug("Failed to find replace file %p\n", search);
+        grub_file_close(file);
+        return 1;
+    }
+    
+    debug("find replace file at %p\n", search);
+
+    grub_memcpy(&g_old_hash, search->hash.sha1, sizeof(wim_hash));
+
+    debug("read lookup offset:%llu size:%llu\n", (ulonglong)head->lookup.offset, (ulonglong)head->lookup.raw_size);
+    lookup = grub_malloc(head->lookup.raw_size);
+    grub_file_seek(file, head->lookup.offset);
+    grub_file_read(file, lookup, head->lookup.raw_size);
+
+    /* find and extact winpeshl.exe */
+    g_replace_look = ventoy_find_look_entry(head, lookup, &g_old_hash);
+    if (g_replace_look)
+    {
+        exe_len = (grub_uint32_t)g_replace_look->resource.raw_size;
+        debug("find replace lookup entry_id:%ld raw_size:%u\n", 
+            ((long)g_replace_look - (long)lookup) / sizeof(wim_lookup_entry), exe_len);
+
+        if (0 == ventoy_read_resource(file, &(g_replace_look->resource), (void **)&(exe_data)))
+        {
+            ventoy_cat_exe_file_data(exe_len, exe_data);
+            grub_free(exe_data);
+        }
+        else
+        {
+            debug("failed to read replace file meta data %u\n", exe_len);
+        }
+    }
+    else
+    {
+        debug("failed to find lookup entry for replace file 0x%02x 0x%02x\n", g_old_hash.sha1[0], g_old_hash.sha1[1]);
+    }
+
+    g_wim_data.wim_raw_size = (grub_uint32_t)file->size;
+    g_wim_data.wim_align_size = ventoy_align(g_wim_data.wim_raw_size, 2048);
+    
+    check_free(g_wim_data.new_meta_data, grub_free);
+    g_wim_data.new_meta_data = decompress_data;
+    g_wim_data.new_meta_len = head->metadata.raw_size;
+    g_wim_data.new_meta_align_len = ventoy_align(g_wim_data.new_meta_len, 2048);
+    
+    check_free(g_wim_data.new_lookup_data, grub_free);
+    g_wim_data.new_lookup_data = (grub_uint8_t *)lookup;
+    g_wim_data.new_lookup_len = (grub_uint32_t)head->lookup.raw_size;
+    g_wim_data.new_lookup_align_len = ventoy_align(g_wim_data.new_lookup_len, 2048);
+
+    head->metadata.flags = RESHDR_FLAG_METADATA;
+    head->metadata.offset = g_wim_data.wim_align_size + g_wim_data.bin_align_len;
+    head->metadata.size_in_wim = g_wim_data.new_meta_len;
+    head->metadata.raw_size = g_wim_data.new_meta_len;
+
+    head->lookup.flags = 0;
+    head->lookup.offset = head->metadata.offset + g_wim_data.new_meta_align_len;
+    head->lookup.size_in_wim = g_wim_data.new_lookup_len;
+    head->lookup.raw_size = g_wim_data.new_lookup_len;
+
+    grub_file_close(file);
+
+    debug("%s", "windows locate wim finish\n");
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+static grub_uint32_t ventoy_get_override_chunk_num(void)
+{
+    /* 1: block count in Partition Descriptor */
+    /* 2: file_size in file_entry or extend_file_entry */
+    /* 3: data_size and position in extend data short ad */
+    /* 4: new wim file header */
+    return 4;
+}
+
+static void ventoy_windows_fill_override_data(    grub_uint64_t isosize, void *override)
+{
+    grub_uint32_t data32;
+    grub_uint64_t data64;
+    grub_uint64_t sector;
+    grub_uint32_t new_wim_size;
+    ventoy_override_chunk *cur;
+
+    sector = (isosize + 2047) / 2048;
+
+    cur = (ventoy_override_chunk *)override;
+
+    new_wim_size = g_wim_data.wim_align_size + g_wim_data.bin_align_len + 
+        g_wim_data.new_meta_align_len + g_wim_data.new_lookup_align_len;
+
+    if (g_wim_data.iso_type == 0)
+    {
+        ventoy_iso9660_override *dirent = (ventoy_iso9660_override *)g_wim_data.override_data;
+
+        dirent->first_sector    = (grub_uint32_t)sector;
+        dirent->size            = new_wim_size;
+        dirent->first_sector_be = grub_swap_bytes32(dirent->first_sector);
+        dirent->size_be         = grub_swap_bytes32(dirent->size);
+    }
+    else
+    {
+        ventoy_udf_override *udf = (ventoy_udf_override *)g_wim_data.override_data;
+        udf->length   = new_wim_size;
+        udf->position = (grub_uint32_t)sector - g_wim_data.udf_start_block;
+    }
+
+    //override 1: sector number in pd data 
+    cur->img_offset = grub_udf_get_last_pd_size_offset();
+    cur->override_size = 4;
+    data32 = sector - g_wim_data.udf_start_block + (new_wim_size / 2048);
+    grub_memcpy(cur->override_data, &(data32), 4);
+
+    //override 2: filesize in file_entry
+    cur++;
+    cur->img_offset = g_wim_data.fe_entry_size_offset;
+    cur->override_size = 8;
+    data64 = new_wim_size;
+    grub_memcpy(cur->override_data, &(data64), 8);
+
+    /* override 3: position and length in extend data */
+    cur++;
+    cur->img_offset = g_wim_data.override_offset;
+    cur->override_size = g_wim_data.override_len;
+    grub_memcpy(cur->override_data, g_wim_data.override_data, cur->override_size);
+
+    /* override 4: new wim file header */
+    cur++;
+    cur->img_offset = g_wim_data.file_offset;
+    cur->override_size = sizeof(wim_header);
+    grub_memcpy(cur->override_data, &(g_wim_data.wim_header), cur->override_size);
+
+    return;
+}
+
+static void ventoy_windows_fill_virt_data(    grub_uint64_t isosize, ventoy_chain_head *chain)
+{
+    grub_uint64_t sector;
+    grub_uint32_t offset;
+    grub_uint32_t wim_secs;
+    grub_uint32_t mem_secs;
+    char *override = NULL;
+    ventoy_virt_chunk *cur = NULL;
+
+    sector = (isosize + 2047) / 2048;
+    offset = sizeof(ventoy_virt_chunk);
+    
+    wim_secs = g_wim_data.wim_align_size / 2048;
+    mem_secs = (g_wim_data.bin_align_len + g_wim_data.new_meta_align_len + g_wim_data.new_lookup_align_len) / 2048;
+
+    override = (char *)chain + chain->virt_chunk_offset;
+    cur = (ventoy_virt_chunk *)override;
+
+    cur->remap_sector_start = sector;
+    cur->remap_sector_end   = cur->remap_sector_start + wim_secs;
+    cur->org_sector_start   = (grub_uint32_t)(g_wim_data.file_offset / 2048);
+    
+    cur->mem_sector_start   = cur->remap_sector_end;
+    cur->mem_sector_end     = cur->mem_sector_start + mem_secs;
+    cur->mem_sector_offset  = offset;
+
+    grub_memcpy(override + offset, g_wim_data.jump_bin_data, g_wim_data.bin_raw_len);
+    offset += g_wim_data.bin_align_len;
+
+    grub_memcpy(override + offset, g_wim_data.new_meta_data, g_wim_data.new_meta_len);
+    offset += g_wim_data.new_meta_align_len;
+    
+    grub_memcpy(override + offset, g_wim_data.new_lookup_data, g_wim_data.new_lookup_len);
+    offset += g_wim_data.new_lookup_align_len;
+
+    chain->virt_img_size_in_bytes += g_wim_data.wim_align_size + 
+                                     g_wim_data.bin_align_len + 
+                                     g_wim_data.new_meta_align_len + 
+                                     g_wim_data.new_lookup_align_len;
+    return;
+}
+
+static int ventoy_windows_drive_map(ventoy_chain_head *chain)
+{
+    grub_disk_t disk;
+        
+    debug("drive map begin <%p> ...\n", chain);
+
+    if (chain->disk_drive == 0x80)
+    {
+        disk = grub_disk_open("hd1");
+        if (disk)
+        {
+            grub_disk_close(disk);
+            debug("drive map needed %p\n", disk);
+            chain->drive_map = 0x81;
+        }
+        else
+        {
+            debug("failed to open disk %s\n", "hd1");
+        }
+    }
+    else
+    {
+        debug("no need to map 0x%x\n", chain->disk_drive);
+    }
+
+    return 0;
+}
+
+grub_err_t ventoy_cmd_windows_chain_data(grub_extcmd_context_t ctxt, int argc, char **args)
+{
+    int unknown_image = 0;
+    int ventoy_compatible = 0;
+    grub_uint32_t size = 0;
+    grub_uint64_t isosize = 0;
+    grub_uint32_t boot_catlog = 0;
+    grub_uint32_t img_chunk_size = 0;
+    grub_uint32_t override_size = 0;
+    grub_uint32_t virt_chunk_size = 0;
+    grub_file_t file;
+    grub_disk_t disk;
+    const char *pLastChain = NULL;
+    const char *compatible;
+    ventoy_chain_head *chain;
+    char envbuf[64];
+    
+    (void)ctxt;
+    (void)argc;
+
+    debug("chain data begin <%s> ...\n", args[0]);
+
+    compatible = grub_env_get("ventoy_compatible");
+    if (compatible && compatible[0] == 'Y')
+    {
+        ventoy_compatible = 1;
+    }
+
+    if (NULL == g_img_chunk_list.chunk)
+    {
+        grub_printf("ventoy not ready\n");
+        return 1;
+    }
+
+    if (0 == ventoy_compatible && g_wim_data.new_meta_data == NULL)
+    {
+        unknown_image = 1;
+        debug("Warning: %s was not recognized by Ventoy\n", args[0]);
+    }
+
+    file = ventoy_grub_file_open(VENTOY_FILE_TYPE, "%s", args[0]);
+    if (!file)
+    {
+        return 1;
+    }
+
+    isosize = file->size;
+
+    boot_catlog = ventoy_get_iso_boot_catlog(file);
+    if (boot_catlog)
+    {
+        if (ventoy_is_efi_os() && (!ventoy_has_efi_eltorito(file, boot_catlog)))
+        {
+            grub_env_set("LoadIsoEfiDriver", "on");
+        }
+    }
+    else
+    {
+        if (ventoy_is_efi_os())
+        {
+            grub_env_set("LoadIsoEfiDriver", "on");
+        }
+        else
+        {
+            return grub_error(GRUB_ERR_BAD_ARGUMENT, "File %s is not bootable", args[0]);
+        }
+    }
+
+    img_chunk_size = g_img_chunk_list.cur_chunk * sizeof(ventoy_img_chunk);
+    
+    if (ventoy_compatible || unknown_image)
+    {
+        size = sizeof(ventoy_chain_head) + img_chunk_size;
+    }
+    else
+    {
+        override_size = ventoy_get_override_chunk_num() * sizeof(ventoy_override_chunk);
+        virt_chunk_size = sizeof(ventoy_virt_chunk) + g_wim_data.bin_align_len + 
+            g_wim_data.new_meta_align_len + g_wim_data.new_lookup_align_len;;
+        size = sizeof(ventoy_chain_head) + img_chunk_size + override_size + virt_chunk_size;
+    }
+
+    pLastChain = grub_env_get("vtoy_chain_mem_addr");
+    if (pLastChain)
+    {
+        chain = (ventoy_chain_head *)grub_strtoul(pLastChain, NULL, 16);
+        if (chain)
+        {
+            debug("free last chain memory %p\n", chain);
+            grub_free(chain);
+        }
+    }
+
+    chain = grub_malloc(size);
+    if (!chain)
+    {
+        grub_printf("Failed to alloc chain memory size %u\n", size);
+        grub_file_close(file);
+        return 1;
+    }
+
+    grub_snprintf(envbuf, sizeof(envbuf), "0x%lx", (unsigned long)chain);
+    grub_env_set("vtoy_chain_mem_addr", envbuf);
+    grub_snprintf(envbuf, sizeof(envbuf), "%u", size);
+    grub_env_set("vtoy_chain_mem_size", envbuf);
+
+    grub_memset(chain, 0, sizeof(ventoy_chain_head));
+
+    /* part 1: os parameter */
+    ventoy_fill_os_param(file, &(chain->os_param));
+
+    if (g_wim_data.jump_bin_data && g_wim_data.new_meta_data)
+    {
+        ventoy_update_before_chain(&(chain->os_param));
+    }
+
+    /* part 2: chain head */
+    disk = file->device->disk;
+    chain->disk_drive = disk->id;
+    chain->disk_sector_size = (1 << disk->log_sector_size);
+    chain->real_img_size_in_bytes = file->size;
+    chain->virt_img_size_in_bytes = (file->size + 2047) / 2048 * 2048;
+    chain->boot_catalog = boot_catlog;
+
+    if (!ventoy_is_efi_os())
+    {
+        grub_file_seek(file, boot_catlog * 2048);
+        grub_file_read(file, chain->boot_catalog_sector, sizeof(chain->boot_catalog_sector));
+    }
+
+    /* part 3: image chunk */
+    chain->img_chunk_offset = sizeof(ventoy_chain_head);
+    chain->img_chunk_num = g_img_chunk_list.cur_chunk;
+    grub_memcpy((char *)chain + chain->img_chunk_offset, g_img_chunk_list.chunk, img_chunk_size);
+
+    if (ventoy_compatible || unknown_image)
+    {
+        return 0;
+    }
+
+    if (g_wim_data.new_meta_data == NULL)
+    {
+        return 0;
+    }
+
+    /* part 4: override chunk */
+    chain->override_chunk_offset = chain->img_chunk_offset + img_chunk_size;
+    chain->override_chunk_num = ventoy_get_override_chunk_num();
+    ventoy_windows_fill_override_data(isosize, (char *)chain + chain->override_chunk_offset);
+
+    /* part 5: virt chunk */
+    chain->virt_chunk_offset = chain->override_chunk_offset + override_size;
+    chain->virt_chunk_num = 1;
+    ventoy_windows_fill_virt_data(isosize, chain);
+
+    if (ventoy_is_efi_os() == 0)
+    {
+        ventoy_windows_drive_map(chain);        
+    }
+
+    VENTOY_CMD_RETURN(GRUB_ERR_NONE);
+}
+
+
diff --git a/GRUB2/grub-2.04/grub-core/ventoy/wimboot.h b/GRUB2/grub-2.04/grub-core/ventoy/wimboot.h
new file mode 100644 (file)
index 0000000..5a77eae
--- /dev/null
@@ -0,0 +1,65 @@
+/******************************************************************************
+ * wimboot.h
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef __WIMBOOT_H__
+#define __WIMBOOT_H__
+
+#include <grub/types.h>
+#include <grub/misc.h>
+#include <grub/mm.h>
+#include <grub/err.h>
+#include <grub/dl.h>
+#include <grub/disk.h>
+#include <grub/device.h>
+#include <grub/term.h>
+#include <grub/partition.h>
+#include <grub/file.h>
+#include <grub/normal.h>
+#include <grub/extcmd.h>
+#include <grub/datetime.h>
+#include <grub/i18n.h>
+#include <grub/net.h>
+#include <grub/time.h>
+#include <grub/crypto.h>
+#include <grub/ventoy.h>
+#include "ventoy_def.h"
+
+
+#define size_t grub_size_t
+#define ssize_t grub_ssize_t
+#define memset grub_memset
+#define memcpy grub_memcpy
+
+#define uint8_t   grub_uint8_t
+#define uint16_t  grub_uint16_t
+#define uint32_t  grub_uint32_t
+#define uint64_t  grub_uint64_t
+#define int32_t   grub_int32_t
+
+
+
+#define assert(exp)
+
+//#define DBG grub_printf
+#define DBG(fmt, ...)
+
+const char * huffman_bin ( unsigned long value, unsigned int bits );
+
+#endif
+
diff --git a/GRUB2/grub-2.04/include/grub/ventoy.h b/GRUB2/grub-2.04/include/grub/ventoy.h
new file mode 100644 (file)
index 0000000..da696c3
--- /dev/null
@@ -0,0 +1,209 @@
+/******************************************************************************
+ * ventoy.h
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __VENTOY_H__
+#define __VENTOY_H__
+
+#define COMPILE_ASSERT(expr)  extern char __compile_assert[(expr) ? 1 : -1]
+
+#define VENTOY_COMPATIBLE_STR      "VENTOY COMPATIBLE"
+#define VENTOY_COMPATIBLE_STR_LEN  17
+
+#define VENTOY_GUID { 0x77772020, 0x2e77, 0x6576, { 0x6e, 0x74, 0x6f, 0x79, 0x2e, 0x6e, 0x65, 0x74 }}
+
+#pragma pack(1)
+
+typedef struct ventoy_guid
+{
+    grub_uint32_t   data1;
+    grub_uint16_t   data2;
+    grub_uint16_t   data3;
+    grub_uint8_t    data4[8];
+}ventoy_guid;
+
+
+typedef struct ventoy_image_disk_region
+{
+    grub_uint32_t   image_sector_count; /* image sectors contained in this region (in 2048) */
+    grub_uint32_t   image_start_sector; /* image sector start (in 2048) */
+    grub_uint64_t   disk_start_sector;  /* disk sector start (in 512) */
+}ventoy_image_disk_region;
+
+typedef struct ventoy_image_location
+{
+    ventoy_guid  guid;
+
+    /* image sector size, currently this value is always 2048 */
+    grub_uint32_t   image_sector_size;
+
+    /* disk sector size, normally the value is 512 */
+    grub_uint32_t   disk_sector_size;
+
+    grub_uint32_t   region_count;
+
+    /*
+     * disk region data (region_count)
+     * If the image file has more than one fragments in disk, 
+     * there will be more than one region data here.
+     *
+     */
+    ventoy_image_disk_region regions[1];
+
+    /* ventoy_image_disk_region regions[2~region_count-1] */
+}ventoy_image_location;
+
+
+typedef struct ventoy_os_param
+{
+    ventoy_guid    guid;                  // VENTOY_GUID
+    grub_uint8_t   chksum;                // checksum
+
+    grub_uint8_t   vtoy_disk_guid[16];
+    grub_uint64_t  vtoy_disk_size;       // disk size in bytes
+    grub_uint16_t  vtoy_disk_part_id;    // begin with 1
+    grub_uint16_t  vtoy_disk_part_type;  // 0:exfat   1:ntfs  other: reserved
+    char           vtoy_img_path[384];   // It seems to be enough, utf-8 format
+    grub_uint64_t  vtoy_img_size;        // image file size in bytes
+
+    /* 
+     * Ventoy will write a copy of ventoy_image_location data into runtime memory
+     * this is the physically address and length of that memory.
+     * Address 0 means no such data exist.
+     * Address will be aligned by 4KB.
+     *
+     */
+    grub_uint64_t  vtoy_img_location_addr;
+    grub_uint32_t  vtoy_img_location_len;
+
+    /* 
+     * These 32 bytes are reserved by ventoy.
+     *
+     * vtoy_reserved[0]: vtoy_break_level
+     * vtoy_reserved[1]: vtoy_debug_level
+     *
+     */
+    grub_uint8_t   vtoy_reserved[32];    // Internal use by ventoy
+
+    grub_uint8_t   reserved[31];
+}ventoy_os_param;
+
+#pragma pack()
+
+// compile assert check : sizeof(ventoy_os_param) must be 512
+COMPILE_ASSERT(sizeof(ventoy_os_param) == 512);
+
+
+
+
+
+
+
+#pragma pack(4)
+
+typedef struct ventoy_chain_head
+{
+    ventoy_os_param os_param;
+
+    grub_uint32_t disk_drive;
+    grub_uint32_t drive_map;
+    grub_uint32_t disk_sector_size;
+
+    grub_uint64_t real_img_size_in_bytes;
+    grub_uint64_t virt_img_size_in_bytes;
+    grub_uint32_t boot_catalog;
+    grub_uint8_t  boot_catalog_sector[2048];
+    
+    grub_uint32_t img_chunk_offset;
+    grub_uint32_t img_chunk_num;
+
+    grub_uint32_t override_chunk_offset;
+    grub_uint32_t override_chunk_num;
+
+    grub_uint32_t virt_chunk_offset;
+    grub_uint32_t virt_chunk_num;
+}ventoy_chain_head;
+
+typedef struct ventoy_img_chunk
+{
+    grub_uint32_t img_start_sector; // sector size: 2KB
+    grub_uint32_t img_end_sector;   // included
+
+    grub_uint64_t disk_start_sector; // in disk_sector_size
+    grub_uint64_t disk_end_sector;   // included
+}ventoy_img_chunk;
+
+
+typedef struct ventoy_override_chunk
+{
+    grub_uint64_t img_offset;
+    grub_uint32_t override_size;
+    grub_uint8_t  override_data[512];
+}ventoy_override_chunk;
+
+typedef struct ventoy_virt_chunk
+{
+    grub_uint32_t mem_sector_start;
+    grub_uint32_t mem_sector_end;
+    grub_uint32_t mem_sector_offset;
+    grub_uint32_t remap_sector_start;
+    grub_uint32_t remap_sector_end;
+    grub_uint32_t org_sector_start;
+}ventoy_virt_chunk;
+
+#define DEFAULT_CHUNK_NUM   1024
+typedef struct ventoy_img_chunk_list
+{
+    grub_uint32_t max_chunk;
+    grub_uint32_t cur_chunk;
+    ventoy_img_chunk *chunk;
+}ventoy_img_chunk_list;
+
+
+#pragma pack()
+
+#define ventoy_filt_register grub_file_filter_register
+
+typedef const char * (*grub_env_get_pf)(const char *name);
+
+#pragma pack(1)
+typedef struct ventoy_grub_param
+{
+    grub_env_get_pf grub_env_get;
+}ventoy_grub_param;
+
+#pragma pack()
+
+
+
+int grub_fat_get_file_chunk(grub_uint64_t part_start, grub_file_t file, ventoy_img_chunk_list *chunk_list);
+grub_uint64_t grub_iso9660_get_last_read_pos(grub_file_t file);
+grub_uint64_t grub_iso9660_get_last_file_dirent_pos(grub_file_t file);
+grub_uint64_t grub_udf_get_file_offset(grub_file_t file);
+grub_uint64_t grub_udf_get_last_pd_size_offset(void);
+grub_uint64_t grub_udf_get_last_file_attr_offset
+(
+    grub_file_t file, 
+    grub_uint32_t *startBlock,
+    grub_uint64_t *fe_entry_size_offset
+);
+int ventoy_is_efi_os(void);
+
+#endif /* __VENTOY_H__ */
+
diff --git a/IMG/cpio/sbin/init b/IMG/cpio/sbin/init
new file mode 100644 (file)
index 0000000..3a4a6ab
--- /dev/null
@@ -0,0 +1,75 @@
+#!/ventoy/busybox/tmpsh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+####################################################################
+#                                                                  #
+# Step 1 : extract busybox & set busybox enviroment                #
+#                                                                  #
+####################################################################
+
+export VTOY_ORG_PATH=$PATH
+export VTOY_PATH=/ventoy
+export BUSYBOX_PATH=$VTOY_PATH/busybox
+export VTLOG=$VTOY_PATH/log
+export FIND=$BUSYBOX_PATH/find
+export GREP=$BUSYBOX_PATH/grep
+export EGREP=$BUSYBOX_PATH/egrep
+export CAT=$BUSYBOX_PATH/cat
+export AWK=$BUSYBOX_PATH/awk
+export SED=$BUSYBOX_PATH/sed
+export SLEEP=$BUSYBOX_PATH/sleep
+export HEAD=$BUSYBOX_PATH/head
+
+$BUSYBOX_PATH/tmpxz -d $BUSYBOX_PATH/busybox.xz
+$BUSYBOX_PATH/busybox --install $BUSYBOX_PATH
+
+export PATH=$BUSYBOX_PATH/:$VTOY_PATH/tool
+
+export VTOY_BREAK_LEVEL=$(hexdump -n 1 -s 429 -e '1/1 "%02x"' $VTOY_PATH/ventoy_os_param)
+export VTOY_DEBUG_LEVEL=$(hexdump -n 1 -s 430 -e '1/1 "%02x"' $VTOY_PATH/ventoy_os_param)
+
+#Fixme: busybox shell output redirect seems to have some bug in rhel5
+if uname -a | grep -q el5; then
+    VTOY_REDT_BUG=YES
+fi
+
+if [ -z "$VTOY_REDT_BUG" ]; then
+    echo "============== VENTOY =================" >>$VTLOG
+fi
+
+cd $VTOY_PATH
+xz -d ventoy.sh.xz
+
+if [ -n "$VTOY_REDT_BUG" ]; then
+    xz -d -c hook.cpio.xz | cpio -idm
+    xz -d -c tool.cpio.xz | cpio -idm
+else
+    xz -d -c hook.cpio.xz | cpio -idm 2>>$VTLOG
+    xz -d -c tool.cpio.xz | cpio -idm 2>>$VTLOG
+fi
+
+rm -f *.xz
+cd /
+
+####################################################################
+#                                                                  #
+# Step 2 : Hand over to ventoy init                                #
+#                                                                  #
+####################################################################
+exec $BUSYBOX_PATH/sh $VTOY_PATH/init
diff --git a/IMG/cpio/ventoy/busybox/busybox.xz b/IMG/cpio/ventoy/busybox/busybox.xz
new file mode 100644 (file)
index 0000000..17bd64b
Binary files /dev/null and b/IMG/cpio/ventoy/busybox/busybox.xz differ
diff --git a/IMG/cpio/ventoy/busybox/tmpsh b/IMG/cpio/ventoy/busybox/tmpsh
new file mode 100644 (file)
index 0000000..9944a11
Binary files /dev/null and b/IMG/cpio/ventoy/busybox/tmpsh differ
diff --git a/IMG/cpio/ventoy/busybox/tmpxz b/IMG/cpio/ventoy/busybox/tmpxz
new file mode 100644 (file)
index 0000000..34acebf
Binary files /dev/null and b/IMG/cpio/ventoy/busybox/tmpxz differ
diff --git a/IMG/cpio/ventoy/hook/alpine/udev_disk_hook.sh b/IMG/cpio/ventoy/hook/alpine/udev_disk_hook.sh
new file mode 100644 (file)
index 0000000..930009b
--- /dev/null
@@ -0,0 +1,71 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. /ventoy/hook/ventoy-hook-lib.sh
+
+if [ "$SUBSYSTEM" != "block" ] || [ "$DEVTYPE" != "partition" ]; then    
+    exit 0
+fi
+
+if [ -b /dev/${MDEV:0:-1} ]; then
+    vtlog "/dev/${MDEV:0:-1} exist"
+else
+    $SLEEP 2
+fi
+
+if is_ventoy_hook_finished || not_ventoy_disk "${MDEV:0:-1}"; then
+    exit 0
+fi
+
+PATH=$BUSYBOX_PATH:$VTOY_PATH/tool:$PATH
+
+#
+# longpanda:
+# Alpine initramfs doesn't contain dm-mod or fuse module, 
+# and even the worse, the libpthread.so is not included too.
+# So here we directly dump the modloop squashfs file from disk to rootfs.
+# Fortunately, this file is not too big (< 100MB in alpine 3.11.3).
+# After that:
+#   1. mount the squashfs file
+#   2. find the dm-mod module from the mountpoint and insmod
+#   3. unmount and delete the squashfs file
+#
+
+vtoydm -i -f $VTOY_PATH/ventoy_image_map -d /dev/${MDEV:0:-1} > $VTOY_PATH/iso_file_list
+
+vtLine=$(grep '[-][-] modloop-lts ' $VTOY_PATH/iso_file_list)
+sector=$(echo $vtLine | awk '{print $(NF-1)}')
+length=$(echo $vtLine | awk '{print $NF}')
+
+vtoydm -e -f $VTOY_PATH/ventoy_image_map -d /dev/${MDEV:0:-1} -s $sector -l $length -o /vt_modloop
+
+mkdir -p $VTOY_PATH/mnt
+mount /vt_modloop $VTOY_PATH/mnt
+
+KoModPath=$(find $VTOY_PATH/mnt/ -name 'dm-mod.ko*')
+vtlog "insmod $KoModPath"
+insmod $KoModPath
+
+umount $VTOY_PATH/mnt
+rm -f /vt_modloop
+
+ventoy_udev_disk_common_hook "$MDEV" "noreplace"
+
+# OK finish
+set_ventoy_hook_finish
diff --git a/IMG/cpio/ventoy/hook/alpine/ventoy-hook.sh b/IMG/cpio/ventoy/hook/alpine/ventoy-hook.sh
new file mode 100644 (file)
index 0000000..ae366cd
--- /dev/null
@@ -0,0 +1,24 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. $VTOY_PATH/hook/ventoy-os-lib.sh
+
+echo "-[-a-z0-9]*2 root:root 0666 @$BUSYBOX_PATH/sh $VTOY_PATH/hook/alpine/udev_disk_hook.sh" >> /mdev.conf
+$CAT /etc/mdev.conf >> /mdev.conf
+$BUSYBOX_PATH/mv /mdev.conf /etc/mdev.conf
diff --git a/IMG/cpio/ventoy/hook/arch/ventoy-hook.sh b/IMG/cpio/ventoy/hook/arch/ventoy-hook.sh
new file mode 100644 (file)
index 0000000..32ee137
--- /dev/null
@@ -0,0 +1,34 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. $VTOY_PATH/hook/ventoy-os-lib.sh
+
+# some archlinux initramfs doesn't contain device-mapper udev rules file
+ARCH_UDEV_DIR=$(ventoy_get_udev_conf_dir)
+if [ -s "$ARCH_UDEV_DIR/13-dm-disk.rules" ]; then
+    echo 'dm-disk rule exist' >> $VTLOG
+else
+    echo 'Copy dm-disk rule file' >> $VTLOG
+    $CAT $VTOY_PATH/hook/default/13-dm-disk.rules > "$ARCH_UDEV_DIR/13-dm-disk.rules"
+fi
+
+# use default proc
+ventoy_systemd_udevd_work_around
+
+ventoy_add_udev_rule "$VTOY_PATH/hook/default/udev_disk_hook.sh %k"
diff --git a/IMG/cpio/ventoy/hook/debian/antix-disk.sh b/IMG/cpio/ventoy/hook/debian/antix-disk.sh
new file mode 100644 (file)
index 0000000..31ff1ca
--- /dev/null
@@ -0,0 +1,117 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. /ventoy/hook/ventoy-hook-lib.sh
+
+vtlog "####### $0 $* ########"
+
+VTPATH_OLD=$PATH; PATH=$BUSYBOX_PATH:$VTOY_PATH/tool:$PATH
+
+
+ventoy_os_install_dmsetup_by_unsquashfs() {
+    vtlog "ventoy_os_install_dmsetup_by_unsquashfs $*"
+    
+    vtKoPo=$(ventoy_get_module_postfix)
+    vtlog "vtKoPo=$vtKoPo"
+
+    vtoydm -i -f $VTOY_PATH/ventoy_image_map -d $1 > $VTOY_PATH/iso_file_list
+
+    vtline=$(grep '[-][-] linuxfs '  $VTOY_PATH/iso_file_list)    
+    sector=$(echo $vtline | awk '{print $(NF-1)}')
+    length=$(echo $vtline | awk '{print $NF}')
+    
+    vtoydm -E -f $VTOY_PATH/ventoy_image_map -d $1 -s $sector -l $length -o $VTOY_PATH/fsdisk
+    
+    dmModPath="/usr/lib/modules/$vtKerVer/kernel/drivers/md/dm-mod.$vtKoPo"
+    echo $dmModPath > $VTOY_PATH/fsextract
+    vtoy_unsquashfs -d $VTOY_PATH/sqfs -n -q -e $VTOY_PATH/fsextract $VTOY_PATH/fsdisk
+
+    if ! [ -e $VTOY_PATH/sqfs${dmModPath} ]; then
+        dmModPath="/lib/modules/$vtKerVer/kernel/drivers/md/dm-mod.$vtKoPo"
+        echo $dmModPath > $VTOY_PATH/fsextract
+        vtoy_unsquashfs -d $VTOY_PATH/sqfs -n -q -e $VTOY_PATH/fsextract $VTOY_PATH/fsdisk
+    fi
+    
+    if [ -e $VTOY_PATH/sqfs${dmModPath} ]; then
+        vtlog "success $VTOY_PATH/sqfs${dmModPath}"
+        insmod $VTOY_PATH/sqfs${dmModPath}
+    else
+        false
+    fi
+}
+
+
+ventoy_os_install_dmsetup_by_fuse() {
+    vtlog "ventoy_os_install_dmsetup_by_fuse $*"
+
+    mkdir -p $VTOY_PATH/mnt/fuse $VTOY_PATH/mnt/iso $VTOY_PATH/mnt/squashfs
+
+    vtoydm -p -f $VTOY_PATH/ventoy_image_map -d $1 > $VTOY_PATH/ventoy_dm_table
+    vtoy_fuse_iso -f $VTOY_PATH/ventoy_dm_table -m $VTOY_PATH/mnt/fuse
+
+    mount -t iso9660  $VTOY_PATH/mnt/fuse/ventoy.iso    $VTOY_PATH/mnt/iso
+    mount -t squashfs $VTOY_PATH/mnt/iso/antiX/linuxfs  $VTOY_PATH/mnt/squashfs
+
+    KoName=$(ls $VTOY_PATH/mnt/squashfs/lib/modules/$2/kernel/drivers/md/dm-mod.ko*)
+    vtlog "insmod $KoName"
+    insmod $KoName
+
+    umount $VTOY_PATH/mnt/squashfs
+    umount $VTOY_PATH/mnt/iso
+    umount $VTOY_PATH/mnt/fuse
+}
+
+
+ventoy_os_install_dmsetup() {
+    vtlog "ventoy_os_install_dmsetup"
+    
+    if grep -q 'device-mapper' /proc/devices; then
+        vtlog "device-mapper module already loaded"
+        return;
+    fi
+    
+    vtKerVer=$(uname -r)
+    
+    if ventoy_os_install_dmsetup_by_unsquashfs $1 $vtKerVer; then
+        vtlog "unsquashfs success"
+    else
+        if modprobe fuse 2>>$VTLOG; then
+            ventoy_os_install_dmsetup_by_fuse $1 $vtKerVer
+        fi
+    fi
+}
+
+vtdiskname=$(get_ventoy_disk_name)
+if [ "$vtdiskname" = "unknown" ]; then
+    vtlog "ventoy disk not found"
+    PATH=$VTPATH_OLD
+    exit 0
+fi
+
+ventoy_os_install_dmsetup $vtdiskname
+
+ventoy_udev_disk_common_hook "${vtdiskname#/dev/}2" "noreplace"
+
+if ! [ -e $VTOY_DM_PATH ]; then
+    blkdev_num=$($VTOY_PATH/tool/dmsetup ls | grep ventoy | sed 's/.*(\([0-9][0-9]*\),.*\([0-9][0-9]*\).*/\1 \2/')
+    mknod -m 0666 $VTOY_DM_PATH b $blkdev_num
+fi
+
+PATH=$VTPATH_OLD
+
diff --git a/IMG/cpio/ventoy/hook/debian/antix-hook.sh b/IMG/cpio/ventoy/hook/debian/antix-hook.sh
new file mode 100644 (file)
index 0000000..6661504
--- /dev/null
@@ -0,0 +1,29 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+if $GREP -q 'FILTERED_LIST=[^a-zA-Z0-9_]*$' /init; then
+    $SED 's#FILTERED_LIST=[^a-zA-Z0-9_]*$#FILTERED_LIST=/dev/mapper/ventoy#' -i /init
+elif $GREP -q '\[ "$FILTERED_LIST" \]' /init; then
+    $SED '/\[ "$FILTERED_LIST" \]/i\    FILTERED_LIST="/dev/mapper/ventoy $FILTERED_LIST"' -i /init
+fi
+
+$SED -i "/_search_for_boot_device_/a\ $BUSYBOX_PATH/sh $VTOY_PATH/hook/debian/antix-disk.sh" /init
+
+# for debug
+#$SED -i "/^linuxfs_error/a\exec $VTOY_PATH/busybox/sh" /init
diff --git a/IMG/cpio/ventoy/hook/debian/default-hook.sh b/IMG/cpio/ventoy/hook/debian/default-hook.sh
new file mode 100644 (file)
index 0000000..a8fd7ec
--- /dev/null
@@ -0,0 +1,21 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+ventoy_systemd_udevd_work_around
+ventoy_add_udev_rule "$VTOY_PATH/hook/debian/udev_disk_hook.sh %k"
diff --git a/IMG/cpio/ventoy/hook/debian/udev_disk_hook.sh b/IMG/cpio/ventoy/hook/debian/udev_disk_hook.sh
new file mode 100644 (file)
index 0000000..7edcbe7
--- /dev/null
@@ -0,0 +1,111 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. /ventoy/hook/ventoy-hook-lib.sh
+
+ventoy_os_install_dmsetup() {
+
+    vt_usb_disk=$1
+
+    # dump iso file location
+    $VTOY_PATH/tool/vtoydm -i -f $VTOY_PATH/ventoy_image_map -d ${vt_usb_disk} > $VTOY_PATH/iso_file_list
+
+    # install dmsetup 
+    LINE=$($GREP ' dmsetup.*\.udeb'  $VTOY_PATH/iso_file_list)
+    if [ $? -eq 0 ]; then
+        install_udeb_from_line "$LINE" ${vt_usb_disk}
+    fi
+
+    # install libdevmapper
+    LINE=$($GREP ' libdevmapper.*\.udeb'  $VTOY_PATH/iso_file_list)
+    if [ $? -eq 0 ]; then
+        install_udeb_from_line "$LINE" ${vt_usb_disk}
+    fi
+
+    # install md-modules
+    LINE=$($GREP ' md-modules.*\.udeb'  $VTOY_PATH/iso_file_list)
+    if [ $? -eq 0 ]; then
+        install_udeb_from_line "$LINE" ${vt_usb_disk} 
+    fi
+
+    # insmod md-mod if needed
+    if $GREP -q 'device-mapper' /proc/devices; then
+        vtlog "device mapper module is loaded"
+    else
+        vtlog"device mapper module is NOT loaded, now load it..."
+        
+        VER=$($BUSYBOX_PATH/uname -r)    
+        KO=$($FIND /lib/modules/$VER/kernel/drivers/md -name "dm-mod*")
+        vtlog "KO=$KO"
+        
+        insmod $KO
+    fi
+    
+    vtlog "dmsetup install finish, now check it..."
+    if dmsetup info >> $VTLOG 2>&1; then
+        vtlog "dmsetup work ok"
+    else
+        vtlog "dmsetup not work, now try to load eglibc ..."
+        
+        # install eglibc (some ubuntu 32 bit version need it)
+        LINE=$($GREP 'libc6-.*\.udeb'  $VTOY_PATH/iso_file_list)
+        if [ $? -eq 0 ]; then
+            install_udeb_from_line "$LINE" ${vt_usb_disk} 
+        fi
+        
+        if dmsetup info >> $VTLOG 2>&1; then
+            vtlog "dmsetup work ok after retry"
+        else
+            vtlog "dmsetup still not work after retry"
+        fi
+    fi
+}
+
+if is_ventoy_hook_finished || not_ventoy_disk "${1:0:-1}"; then
+    exit 0
+fi
+
+dmsetup_path=$(ventoy_find_bin_path dmsetup)
+if [ -z "$dmsetup_path" ]; then
+    ventoy_os_install_dmsetup "/dev/${1:0:-1}"
+fi
+
+ventoy_udev_disk_common_hook $*
+
+#
+# Some distro default only accept usb partitions as install medium.
+# So if ventoy is installed on a non-USB device, we just mount /cdrom here except
+# for these has boot=live or boot=casper parameter in cmdline
+#
+if echo $ID_BUS | $GREP -q -i usb; then
+    vtlog "$1 is USB device"
+else
+    vtlog "$1 is NOT USB device (bus $ID_BUS)"
+    
+    if $EGREP -q 'boot=|casper' /proc/cmdline; then
+        vtlog "boot=, or casper, don't mount"
+    else
+        vtlog "No boot param, need to mount"
+        $BUSYBOX_PATH/mkdir /cdrom
+        $BUSYBOX_PATH/mount -t iso9660 $VTOY_DM_PATH  /cdrom
+    fi
+fi
+
+# OK finish
+set_ventoy_hook_finish
diff --git a/IMG/cpio/ventoy/hook/debian/ventoy-hook.sh b/IMG/cpio/ventoy/hook/debian/ventoy-hook.sh
new file mode 100644 (file)
index 0000000..22e2790
--- /dev/null
@@ -0,0 +1,31 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. $VTOY_PATH/hook/ventoy-os-lib.sh
+
+DISTRO='default'
+
+if [ -e /etc/initrd-release ]; then
+    if $EGREP -q "ID=.*antix|ID=.*mx" /etc/initrd-release; then
+        DISTRO='antix'
+    fi
+fi
+
+echo "##### distribution = $DISTRO ######" >> $VTLOG
+. $VTOY_PATH/hook/debian/${DISTRO}-hook.sh
diff --git a/IMG/cpio/ventoy/hook/default/13-dm-disk.rules b/IMG/cpio/ventoy/hook/default/13-dm-disk.rules
new file mode 100644 (file)
index 0000000..9be707a
--- /dev/null
@@ -0,0 +1,42 @@
+# Copyright (C) 2009 Red Hat, Inc. All rights reserved.
+#
+# This file is part of LVM2.
+
+# Udev rules for device-mapper devices.
+#
+# These rules create symlinks in /dev/disk directory.
+# Symlinks that depend on probing filesystem type,
+# label and uuid are created only if the device is not
+# suspended.
+
+# "add" event is processed on coldplug only!
+ACTION!="add|change", GOTO="dm_end"
+ENV{DM_UDEV_RULES_VSN}!="?*", GOTO="dm_end"
+ENV{DM_UDEV_DISABLE_DISK_RULES_FLAG}=="1", GOTO="dm_end"
+
+SYMLINK+="disk/by-id/dm-name-$env{DM_NAME}"
+ENV{DM_UUID}=="?*", SYMLINK+="disk/by-id/dm-uuid-$env{DM_UUID}"
+
+ENV{DM_SUSPENDED}=="1", GOTO="dm_end"
+ENV{DM_NOSCAN}=="1", GOTO="dm_watch"
+
+IMPORT{builtin}="blkid"
+ENV{DM_UDEV_LOW_PRIORITY_FLAG}=="1", OPTIONS="link_priority=-100"
+ENV{ID_FS_USAGE}=="filesystem|other|crypto", ENV{ID_FS_UUID_ENC}=="?*", SYMLINK+="disk/by-uuid/$env{ID_FS_UUID_ENC}"
+ENV{ID_FS_USAGE}=="filesystem|other", ENV{ID_FS_LABEL_ENC}=="?*", SYMLINK+="disk/by-label/$env{ID_FS_LABEL_ENC}"
+ENV{ID_PART_ENTRY_UUID}=="?*", SYMLINK+="disk/by-partuuid/$env{ID_PART_ENTRY_UUID}"
+ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_ENTRY_NAME}=="?*", SYMLINK+="disk/by-partlabel/$env{ID_PART_ENTRY_NAME}"
+ENV{ID_PART_ENTRY_SCHEME}=="gpt", ENV{ID_PART_GPT_AUTO_ROOT}=="1", SYMLINK+="gpt-auto-root"
+
+# Add inotify watch to track changes on this device.
+# Using the watch rule is not optimal - it generates a lot of spurious
+# and useless events whenever the device opened for read-write is closed.
+# The best would be to generete the event directly in the tool changing
+# relevant information so only relevant events will be processed
+# (like creating a filesystem, changing filesystem label etc.).
+#
+# But let's use this until we have something better...
+LABEL="dm_watch"
+OPTIONS+="watch"
+
+LABEL="dm_end"
diff --git a/IMG/cpio/ventoy/hook/default/udev_disk_hook.sh b/IMG/cpio/ventoy/hook/default/udev_disk_hook.sh
new file mode 100644 (file)
index 0000000..633f29e
--- /dev/null
@@ -0,0 +1,30 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. /ventoy/hook/ventoy-hook-lib.sh
+
+if is_ventoy_hook_finished || not_ventoy_disk "${1:0:-1}"; then
+    exit 0
+fi
+
+ventoy_udev_disk_common_hook $*
+
+# OK finish
+set_ventoy_hook_finish
+
diff --git a/IMG/cpio/ventoy/hook/default/ventoy-hook.sh b/IMG/cpio/ventoy/hook/default/ventoy-hook.sh
new file mode 100644 (file)
index 0000000..152dd06
--- /dev/null
@@ -0,0 +1,24 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. $VTOY_PATH/hook/ventoy-os-lib.sh
+
+ventoy_systemd_udevd_work_around
+
+ventoy_add_udev_rule "$VTOY_PATH/hook/default/udev_disk_hook.sh %k"
diff --git a/IMG/cpio/ventoy/hook/gentoo/disk_hook.sh b/IMG/cpio/ventoy/hook/gentoo/disk_hook.sh
new file mode 100644 (file)
index 0000000..3f515bf
--- /dev/null
@@ -0,0 +1,36 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. /ventoy/hook/ventoy-hook-lib.sh
+
+# Just for KVM test enviroment
+$BUSYBOX_PATH/modprobe virtio_blk 2>/dev/null
+$BUSYBOX_PATH/modprobe virtio_pci 2>/dev/null
+
+for i in 0 1 2 3 4 5 6 7 8 9; do 
+    vtdiskname=$(get_ventoy_disk_name)
+    if [ "$vtdiskname" = "unknown" ]; then
+        vtlog "wait for disk ..."
+        $SLEEP 2
+    else
+        break
+    fi
+done
+
+ventoy_udev_disk_common_hook "${vtdiskname#/dev/}2" "noreplace"
diff --git a/IMG/cpio/ventoy/hook/gentoo/ventoy-hook.sh b/IMG/cpio/ventoy/hook/gentoo/ventoy-hook.sh
new file mode 100644 (file)
index 0000000..d9034fb
--- /dev/null
@@ -0,0 +1,27 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. $VTOY_PATH/hook/ventoy-os-lib.sh
+
+if [ -d /etc/udev/rules.d ]; then    
+    ventoy_systemd_udevd_work_around
+    ventoy_add_udev_rule "$VTOY_PATH/hook/default/udev_disk_hook.sh %k noreplace"
+else
+    $SED "/mdev *-s/a\ $BUSYBOX_PATH/sh $VTOY_PATH/hook/gentoo/disk_hook.sh"  -i /init
+fi
diff --git a/IMG/cpio/ventoy/hook/kaos/udev_disk_hook.sh b/IMG/cpio/ventoy/hook/kaos/udev_disk_hook.sh
new file mode 100644 (file)
index 0000000..08ee268
--- /dev/null
@@ -0,0 +1,39 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. /ventoy/hook/ventoy-hook-lib.sh
+
+if is_ventoy_hook_finished || not_ventoy_disk "${1:0:-1}"; then
+    exit 0
+fi
+
+VTPATH_OLD=$PATH; PATH=$BUSYBOX_PATH:$VTOY_PATH/tool:$PATH
+
+modprobe fuse
+mkdir -p $VTOY_PATH/mnt/fuse $VTOY_PATH/mnt/iso
+
+vtoydm -p -f $VTOY_PATH/ventoy_image_map -d "/dev/${1:0:-1}" > $VTOY_PATH/ventoy_dm_table
+vtoy_fuse_iso -f $VTOY_PATH/ventoy_dm_table -m $VTOY_PATH/mnt/fuse
+mount -t iso9660 $VTOY_PATH/mnt/fuse/ventoy.iso  $VTOY_PATH/mnt/iso
+
+# OK finish
+set_ventoy_hook_finish
+
+PATH=$VTPATH_OLD
+
diff --git a/IMG/cpio/ventoy/hook/kaos/ventoy-hook.sh b/IMG/cpio/ventoy/hook/kaos/ventoy-hook.sh
new file mode 100644 (file)
index 0000000..c268801
--- /dev/null
@@ -0,0 +1,6 @@
+#!/ventoy/busybox/sh
+
+. $VTOY_PATH/hook/ventoy-os-lib.sh
+
+ventoy_systemd_udevd_work_around
+ventoy_add_udev_rule "$VTOY_PATH/hook/kaos/udev_disk_hook.sh %k"
diff --git a/IMG/cpio/ventoy/hook/mageia/udev_disk_hook.sh b/IMG/cpio/ventoy/hook/mageia/udev_disk_hook.sh
new file mode 100644 (file)
index 0000000..b685c71
--- /dev/null
@@ -0,0 +1,40 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. /ventoy/hook/ventoy-hook-lib.sh
+
+if is_ventoy_hook_finished || not_ventoy_disk "${1:0:-1}"; then
+    exit 0
+fi
+
+ventoy_udev_disk_common_hook $*
+
+#
+# cheatcode for mageia
+#
+# From mageia/soft/drakx/mdk-stage1 source code, we see that the stage1 binary will search 
+# /tmp/syslog file to determin whether there is a DAC960 cdrom in the system.
+# So we insert some string to /tmp/syslog file to cheat the stage1 program.
+#
+$BUSYBOX_PATH/mkdir -p /dev/rd
+ventoy_copy_device_mapper "/dev/rd/ventoy"
+echo 'ventoy cheatcode /dev/rd/ventoy:  model' >> /tmp/syslog
+
+# OK finish
+set_ventoy_hook_finish
diff --git a/IMG/cpio/ventoy/hook/mageia/ventoy-hook.sh b/IMG/cpio/ventoy/hook/mageia/ventoy-hook.sh
new file mode 100644 (file)
index 0000000..5edc8f2
--- /dev/null
@@ -0,0 +1,7 @@
+#!/ventoy/busybox/sh
+
+. $VTOY_PATH/hook/ventoy-os-lib.sh
+
+ventoy_systemd_udevd_work_around
+
+ventoy_add_udev_rule "$VTOY_PATH/hook/mageia/udev_disk_hook.sh %k noreplace"
diff --git a/IMG/cpio/ventoy/hook/manjaro/ventoy-hook.sh b/IMG/cpio/ventoy/hook/manjaro/ventoy-hook.sh
new file mode 100644 (file)
index 0000000..b902f98
--- /dev/null
@@ -0,0 +1,33 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. $VTOY_PATH/hook/ventoy-os-lib.sh
+
+# some distro initramfs doesn't contain device-mapper udev rules file
+DISTRO_UDEV_DIR=$(ventoy_get_udev_conf_dir)
+if [ -s "$DISTRO_UDEV_DIR/13-dm-disk.rules" ]; then
+    echo 'dm-disk rule exist' >> $VTLOG
+else
+    echo 'Copy dm-disk rule file' >> $VTLOG
+    $CAT $VTOY_PATH/hook/default/13-dm-disk.rules > "$DISTRO_UDEV_DIR/13-dm-disk.rules"
+fi
+
+ventoy_systemd_udevd_work_around
+
+ventoy_add_udev_rule "$VTOY_PATH/hook/default/udev_disk_hook.sh %k"
diff --git a/IMG/cpio/ventoy/hook/nixos/udev_disk_hook.sh b/IMG/cpio/ventoy/hook/nixos/udev_disk_hook.sh
new file mode 100644 (file)
index 0000000..633f29e
--- /dev/null
@@ -0,0 +1,30 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. /ventoy/hook/ventoy-hook-lib.sh
+
+if is_ventoy_hook_finished || not_ventoy_disk "${1:0:-1}"; then
+    exit 0
+fi
+
+ventoy_udev_disk_common_hook $*
+
+# OK finish
+set_ventoy_hook_finish
+
diff --git a/IMG/cpio/ventoy/hook/nixos/ventoy-hook.sh b/IMG/cpio/ventoy/hook/nixos/ventoy-hook.sh
new file mode 100644 (file)
index 0000000..7b01c13
--- /dev/null
@@ -0,0 +1,25 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. $VTOY_PATH/hook/ventoy-os-lib.sh
+
+vtRuleFile=$($FIND / -name '*.rules' -type f | $GREP udev | $HEAD -n1)
+
+ventoy_add_udev_rule_with_path "$VTOY_PATH/hook/default/udev_disk_hook.sh %k noreplace" "${vtRuleFile%/*}/99-ventoy.rules"
+
diff --git a/IMG/cpio/ventoy/hook/pclos/disk_hook.sh b/IMG/cpio/ventoy/hook/pclos/disk_hook.sh
new file mode 100644 (file)
index 0000000..ca91ac8
--- /dev/null
@@ -0,0 +1,72 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. /ventoy/hook/ventoy-hook-lib.sh
+
+VTPATH_OLD=$PATH; PATH=$BUSYBOX_PATH:$VTOY_PATH/tool:$PATH
+
+ventoy_os_install_device_mapper_by_unsquashfs() {
+    vtlog "ventoy_os_install_device_mapper_by_unsquashfs $*"
+    
+    vtKoExt=$(ventoy_get_module_postfix)
+    vtlog "vtKoExt=$vtKoExt"
+
+    vtoydm -i -f $VTOY_PATH/ventoy_image_map -d $1 > $VTOY_PATH/iso_file_list
+
+    vtline=$(grep '[-][-] livecd.sqfs '  $VTOY_PATH/iso_file_list)    
+    sector=$(echo $vtline | awk '{print $(NF-1)}')
+    length=$(echo $vtline | awk '{print $NF}')
+    
+    vtoydm -E -f $VTOY_PATH/ventoy_image_map -d $1 -s $sector -l $length -o $VTOY_PATH/fsdisk
+    
+    dmModPath="/lib/modules/$2/kernel/drivers/md/dm-mod.$vtKoExt"
+    echo $dmModPath > $VTOY_PATH/fsextract
+    vtoy_unsquashfs -d $VTOY_PATH/sqfs -n -q -e $VTOY_PATH/fsextract $VTOY_PATH/fsdisk
+
+    if [ -e $VTOY_PATH/sqfs${dmModPath} ]; then
+        vtlog "success $VTOY_PATH/sqfs${dmModPath}"
+        insmod $VTOY_PATH/sqfs${dmModPath}
+    else
+        false
+    fi
+}
+
+
+ventoy_os_install_device_mapper() {
+    vtlog "ventoy_os_install_device_mapper"
+    
+    if grep -q 'device-mapper' /proc/devices; then
+        vtlog "device-mapper module already loaded"
+        return;
+    fi
+    
+    vtKerVer=$(uname -r)
+    if ventoy_os_install_device_mapper_by_unsquashfs $1 $vtKerVer; then
+        vtlog "unsquashfs success"
+    else
+        vterr "unsquashfs failed"
+    fi
+}
+
+vtdiskname=$(get_ventoy_disk_name)
+ventoy_os_install_device_mapper $vtdiskname
+
+ventoy_udev_disk_common_hook "${vtdiskname#/dev/}2"
+
+PATH=$VTPATH_OLD
diff --git a/IMG/cpio/ventoy/hook/pclos/ventoy-hook.sh b/IMG/cpio/ventoy/hook/pclos/ventoy-hook.sh
new file mode 100644 (file)
index 0000000..d082453
--- /dev/null
@@ -0,0 +1,57 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. $VTOY_PATH/hook/ventoy-os-lib.sh
+
+# Step 1: dd initrd file to ramdisk
+vtRamdiskFile=$($BUSYBOX_PATH/ls /initrd* | $HEAD -n1)
+$BUSYBOX_PATH/mknod -m 0666 /ram0 b 1 0
+$BUSYBOX_PATH/dd if=$vtRamdiskFile of=/ram0 status=none
+$BUSYBOX_PATH/rm -f $vtRamdiskFile
+
+# Step 2: mount ramdisk
+$BUSYBOX_PATH/mkdir -p /ventoy_rdroot
+ventoy_close_printk
+$BUSYBOX_PATH/mount /ram0 /ventoy_rdroot
+ventoy_restore_printk
+
+# Step 3: Copy ventoy tool to new root directory.
+# Here we make a tmpfs mount to avoid ramdisk out of space (additional space is for log).
+vtSize=$($BUSYBOX_PATH/du -m -s $VTOY_PATH | $BUSYBOX_PATH/awk '{print $1}')
+let vtSize=vtSize+4
+
+$BUSYBOX_PATH/mkdir -p /ventoy_rdroot/ventoy
+$BUSYBOX_PATH/mount -t tmpfs -o size=${vtSize}m tmpfs /ventoy_rdroot/ventoy
+$BUSYBOX_PATH/cp -a /ventoy/* /ventoy_rdroot/ventoy/
+
+
+# Step 4: add hook in linuxrc&rc.sysinit script file
+vtLine=$($GREP -n "^find_cdrom" /ventoy_rdroot/linuxrc | $GREP -v '(' | $AWK -F: '{print $1}')
+$SED "$vtLine aif test -d /ventoy; then $BUSYBOX_PATH/sh $VTOY_PATH/hook/pclos/disk_hook.sh; fi" -i /ventoy_rdroot/linuxrc
+$SED "$vtLine aif test -d /initrd/ventoy; then ln -s /initrd/ventoy /ventoy; fi" -i /ventoy_rdroot/linuxrc
+
+
+vtRcInit=$($BUSYBOX_PATH/tail /ventoy_rdroot/linuxrc | $GREP 'exec ' | $AWK '{print $2}')
+if [ -e /ventoy_rdroot$vtRcInit ]; then
+    vtRcInit=/ventoy_rdroot$vtRcInit
+else
+    vtRcInit=/ventoy_rdroot/etc/rc.d/rc.sysinit
+fi
+
+echo 'exec /sbin/init' >> $vtRcInit
diff --git a/IMG/cpio/ventoy/hook/rhel5/ventoy-hook.sh b/IMG/cpio/ventoy/hook/rhel5/ventoy-hook.sh
new file mode 100644 (file)
index 0000000..c028991
--- /dev/null
@@ -0,0 +1,23 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+#if [ -e $VTOY_PATH/hook/rhel5/loader ]; then
+#    $BUSYBOX_PATH/cp -a $VTOY_PATH/hook/rhel5/loader  /sbin/loader
+#fi
+#$BUSYBOX_PATH/cp -a $VTOY_PATH/hook/rhel5/ventoy-loader.sh  $VTOY_PATH/tool/
diff --git a/IMG/cpio/ventoy/hook/rhel5/ventoy-loader.sh b/IMG/cpio/ventoy/hook/rhel5/ventoy-loader.sh
new file mode 100644 (file)
index 0000000..3582ed8
--- /dev/null
@@ -0,0 +1,70 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. /ventoy/hook/ventoy-hook-lib.sh
+
+ventoy_os_install_dmsetup() {
+    vtlog "ventoy_os_install_dmsetup $1"
+    
+    vt_usb_disk=$1
+    
+    # dump iso file location
+    $VTOY_PATH/tool/vtoydm -i -f $VTOY_PATH/ventoy_image_map -d ${vt_usb_disk} > $VTOY_PATH/iso_file_list
+
+    # install dmsetup 
+    LINE=$($GREP 'minstg2.img'  $VTOY_PATH/iso_file_list)
+    if [ $? -eq 0 ]; then
+        extract_file_from_line "$LINE" ${vt_usb_disk} /tmp/minstg2.img
+        
+        mkdir -p /tmp/ramfs/minstg2.img
+        mount -t squashfs  /tmp/minstg2.img   /tmp/ramfs/minstg2.img
+        
+        $BUSYBOX_PATH/ln -s /tmp/ramfs/minstg2.img/lib64 /lib64
+        $BUSYBOX_PATH/ln -s /tmp/ramfs/minstg2.img/usr /usr
+    fi
+
+    vtlog "dmsetup install finish, now check it..."
+    
+    dmsetup_path=$(ventoy_find_bin_path dmsetup)
+    if [ -z "$dmsetup_path" ]; then
+        vterr "dmsetup still not found after install"
+    elif $dmsetup_path info >> $VTLOG 2>&1; then
+        vtlog "$dmsetup_path work ok"
+    else
+        vterr "$dmsetup_path not work"
+    fi
+}
+
+vtlog "##### $0 $* ########"
+
+vtdiskname=$(get_ventoy_disk_name)
+
+dmsetup_path=$(ventoy_find_bin_path dmsetup)
+if [ -z "$dmsetup_path" ]; then
+    ventoy_os_install_dmsetup "$vtdiskname"
+    ventoy_udev_disk_common_hook "${vtdiskname#/dev/}2" "noreplace"
+    
+    $BUSYBOX_PATH/unlink /lib64
+    $BUSYBOX_PATH/unlink /usr
+    umount /tmp/ramfs/minstg2.img
+    rm -rf /tmp/ramfs/minstg2.img
+    rm -f /tmp/minstg2.img 
+else
+    ventoy_udev_disk_common_hook "${vtdiskname#/dev/}2" "noreplace"
+fi
diff --git a/IMG/cpio/ventoy/hook/rhel6/anaconda-repo-listen.sh b/IMG/cpio/ventoy/hook/rhel6/anaconda-repo-listen.sh
new file mode 100644 (file)
index 0000000..5cadb79
--- /dev/null
@@ -0,0 +1,20 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+sed -i "s/^enabled.*/enabled=0/g" $2/$3
diff --git a/IMG/cpio/ventoy/hook/rhel6/udev_disk_hook.sh b/IMG/cpio/ventoy/hook/rhel6/udev_disk_hook.sh
new file mode 100644 (file)
index 0000000..388c92c
--- /dev/null
@@ -0,0 +1,85 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. /ventoy/hook/ventoy-hook-lib.sh
+
+ventoy_os_install_dmsetup() {
+    vtlog "ventoy_os_install_dmsetup $1"
+    
+    vt_usb_disk=$1
+
+    $BUSYBOX_PATH/modprobe dm-mod
+    $BUSYBOX_PATH/modprobe linear
+    
+    # dump iso file location
+    $VTOY_PATH/tool/vtoydm -i -f $VTOY_PATH/ventoy_image_map -d ${vt_usb_disk} > $VTOY_PATH/iso_file_list
+
+    # install dmsetup 
+    LINE=$($GREP 'device-mapper-[0-9].*\.rpm'  $VTOY_PATH/iso_file_list)
+    if [ $? -eq 0 ]; then
+        install_rpm_from_line "$LINE" ${vt_usb_disk}
+    fi
+
+    vtlog "dmsetup install finish, now check it..."
+    
+    dmsetup_path=$(ventoy_find_bin_path dmsetup)
+    if [ -z "$dmsetup_path" ]; then
+        vterr "dmsetup still not found after install"
+    elif $dmsetup_path info >> $VTLOG 2>&1; then
+        vtlog "$dmsetup_path work ok"
+    else
+        vterr "$dmsetup_path not work"
+    fi
+}
+
+
+if is_ventoy_hook_finished || not_ventoy_disk "${1:0:-1}"; then
+    # /dev/loop7 come first
+    if [ "$1" = "loop7" ] && [ -b $VTOY_DM_PATH ]; then
+        ventoy_copy_device_mapper  /dev/loop7
+    fi
+    exit 0
+fi
+
+dmsetup_path=$(ventoy_find_bin_path dmsetup)
+if [ -z "$dmsetup_path" ]; then
+    ventoy_os_install_dmsetup "/dev/${1:0:-1}"
+fi
+
+
+#some distro add there repo file to /etc/anaconda.repos.d/ which will cause error during installation
+$BUSYBOX_PATH/nohup $VTOY_PATH/tool/inotifyd $VTOY_PATH/hook/rhel6/anaconda-repo-listen.sh /etc/anaconda.repos.d:n &  
+
+ventoy_udev_disk_common_hook $* "noreplace"
+
+$BUSYBOX_PATH/mount $VTOY_DM_PATH /mnt/ventoy
+
+# 
+# We do a trick for rhel6 series here.
+# Use /dev/loop7 and wapper it as a removable cdrom with bind mount.
+# Then the anaconda installer will accept /dev/loop7 as the install medium.
+#
+ventoy_copy_device_mapper  /dev/loop7
+
+$BUSYBOX_PATH/cp -a /sys/devices/virtual/block/loop7 /tmp/ >> $VTLOG 2>&1
+echo 19 > /tmp/loop7/capability
+$BUSYBOX_PATH/mount --bind /tmp/loop7 /sys/block/loop7 >> $VTLOG 2>&1
+
+# OK finish
+set_ventoy_hook_finish
diff --git a/IMG/cpio/ventoy/hook/rhel6/ventoy-hook.sh b/IMG/cpio/ventoy/hook/rhel6/ventoy-hook.sh
new file mode 100644 (file)
index 0000000..e66dbc5
--- /dev/null
@@ -0,0 +1,27 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. $VTOY_PATH/hook/ventoy-os-lib.sh
+
+$BUSYBOX_PATH/mkdir -p /etc/anaconda.repos.d  /mnt/ventoy
+ventoy_print_yum_repo "ventoy" "file:///mnt/ventoy" > /etc/anaconda.repos.d/ventoy.repo
+
+
+ventoy_add_udev_rule "$VTOY_PATH/hook/rhel6/udev_disk_hook.sh %k"
+ventoy_add_kernel_udev_rule "loop7" "$VTOY_PATH/hook/rhel6/udev_disk_hook.sh %k"
diff --git a/IMG/cpio/ventoy/hook/rhel7/ventoy-hook.sh b/IMG/cpio/ventoy/hook/rhel7/ventoy-hook.sh
new file mode 100644 (file)
index 0000000..6bb0664
--- /dev/null
@@ -0,0 +1,29 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. $VTOY_PATH/hook/ventoy-os-lib.sh
+
+ventoy_systemd_udevd_work_around
+
+ventoy_add_udev_rule "$VTOY_PATH/hook/default/udev_disk_hook.sh %k noreplace"
+
+# suppress write protected mount warning
+if [ -e /usr/sbin/anaconda-diskroot ]; then
+    $SED  's/^mount $dev $repodir/mount -oro $dev $repodir/' -i /usr/sbin/anaconda-diskroot
+fi
diff --git a/IMG/cpio/ventoy/hook/slackware/udev_disk_hook.sh b/IMG/cpio/ventoy/hook/slackware/udev_disk_hook.sh
new file mode 100644 (file)
index 0000000..9b06483
--- /dev/null
@@ -0,0 +1,41 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. /ventoy/hook/ventoy-hook-lib.sh
+
+if is_ventoy_hook_finished || not_ventoy_disk "${1:0:-1}"; then
+    exit 0
+fi
+
+ventoy_udev_disk_common_hook $*
+
+#
+# make a fake cdrom link
+#
+if [ -e /dev/sr3 ]; then
+    if ! [ -e /dev/hdp ]; then
+        ln -s $VTOY_DM_PATH /dev/hdp
+    fi
+else
+    ln -s $VTOY_DM_PATH /dev/sr3
+fi
+
+# OK finish
+set_ventoy_hook_finish
+
diff --git a/IMG/cpio/ventoy/hook/slackware/ventoy-hook.sh b/IMG/cpio/ventoy/hook/slackware/ventoy-hook.sh
new file mode 100644 (file)
index 0000000..570e909
--- /dev/null
@@ -0,0 +1,8 @@
+#!/ventoy/busybox/sh
+
+. $VTOY_PATH/hook/ventoy-os-lib.sh
+
+ventoy_systemd_udevd_work_around
+
+ventoy_add_udev_rule "$VTOY_PATH/hook/slackware/udev_disk_hook.sh %k noreplace"
+
diff --git a/IMG/cpio/ventoy/hook/suse/udev_disk_hook.sh b/IMG/cpio/ventoy/hook/suse/udev_disk_hook.sh
new file mode 100644 (file)
index 0000000..67f3692
--- /dev/null
@@ -0,0 +1,60 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. /ventoy/hook/ventoy-hook-lib.sh
+
+ventoy_os_install_dmsetup() {
+    vtlog "ventoy_os_install_dmsetup $1"
+    
+    vt_usb_disk=$1
+
+    # dump iso file location
+    $VTOY_PATH/tool/vtoydm -i -f $VTOY_PATH/ventoy_image_map -d ${vt_usb_disk} > $VTOY_PATH/iso_file_list
+
+    # install dmsetup 
+    LINE=$($GREP 'device-mapper-[0-9]\..*\.rpm'  $VTOY_PATH/iso_file_list)
+    if [ $? -eq 0 ]; then
+        install_rpm_from_line "$LINE" ${vt_usb_disk}
+    fi
+
+    vtlog "dmsetup install finish, now check it..."
+    
+    dmsetup_path=$(ventoy_find_bin_path dmsetup)
+    if [ -z "$dmsetup_path" ]; then
+        vterr "dmsetup still not found after install"
+    elif $dmsetup_path info >> $VTLOG 2>&1; then
+        vtlog "$dmsetup_path work ok"
+    else
+        vterr "$dmsetup_path not work"
+    fi
+}
+
+if is_ventoy_hook_finished || not_ventoy_disk "${1:0:-1}"; then
+    exit 0
+fi
+
+dmsetup_path=$(ventoy_find_bin_path dmsetup)
+if [ -z "$dmsetup_path" ]; then
+    ventoy_os_install_dmsetup "/dev/${1:0:-1}"
+fi
+
+ventoy_udev_disk_common_hook $*
+
+# OK finish
+set_ventoy_hook_finish
diff --git a/IMG/cpio/ventoy/hook/suse/ventoy-hook.sh b/IMG/cpio/ventoy/hook/suse/ventoy-hook.sh
new file mode 100644 (file)
index 0000000..a281df9
--- /dev/null
@@ -0,0 +1,24 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. $VTOY_PATH/hook/ventoy-os-lib.sh
+
+ventoy_systemd_udevd_work_around
+
+ventoy_add_udev_rule "$VTOY_PATH/hook/suse/udev_disk_hook.sh %k"
diff --git a/IMG/cpio/ventoy/hook/tinycore/udev_disk_hook.sh b/IMG/cpio/ventoy/hook/tinycore/udev_disk_hook.sh
new file mode 100644 (file)
index 0000000..6851668
--- /dev/null
@@ -0,0 +1,45 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. /ventoy/hook/ventoy-hook-lib.sh
+
+if is_ventoy_hook_finished || not_ventoy_disk "${1:0:-1}"; then
+    exit 0
+fi
+
+# TinyCore linux distro doesn't contain dmsetup, we use aoe here
+sudo $BUSYBOX_PATH/modprobe aoe aoe_iflist=lo
+if [ -e /sys/module/aoe ]; then
+    VBLADE_BIN=$(ventoy_get_vblade_bin)
+    sudo $VBLADE_BIN -r -f $VTOY_PATH/ventoy_image_map 9 0 lo "/dev/${1:0:-1}" &
+
+    while ! [ -b /dev/etherd/e9.0 ]; do
+        vtlog 'Wait for /dev/etherd/e9.0 ....'
+        $SLEEP 0.1
+    done
+
+    sudo $BUSYBOX_PATH/cp -a /dev/etherd/e9.0  "/dev/$1"
+
+    ventoy_find_bin_run rebuildfstab
+else
+    vterr "aoe driver module load failed..."
+fi
+
+# OK finish
+set_ventoy_hook_finish
diff --git a/IMG/cpio/ventoy/hook/tinycore/ventoy-hook.sh b/IMG/cpio/ventoy/hook/tinycore/ventoy-hook.sh
new file mode 100644 (file)
index 0000000..b788030
--- /dev/null
@@ -0,0 +1,24 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. $VTOY_PATH/hook/ventoy-os-lib.sh
+
+ventoy_systemd_udevd_work_around
+
+ventoy_add_udev_rule_with_name "$VTOY_PATH/hook/tinycore/udev_disk_hook.sh %k" "90-ventoy.rules"
diff --git a/IMG/cpio/ventoy/hook/ventoy-hook-lib.sh b/IMG/cpio/ventoy/hook/ventoy-hook-lib.sh
new file mode 100644 (file)
index 0000000..c182a54
--- /dev/null
@@ -0,0 +1,424 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+VTOY_PATH=/ventoy
+BUSYBOX_PATH=$VTOY_PATH/busybox
+VTLOG=$VTOY_PATH/log
+FIND=$BUSYBOX_PATH/find
+GREP=$BUSYBOX_PATH/grep
+EGREP=$BUSYBOX_PATH/egrep
+CAT=$BUSYBOX_PATH/cat
+AWK=$BUSYBOX_PATH/awk
+SED=$BUSYBOX_PATH/sed
+SLEEP=$BUSYBOX_PATH/sleep
+HEAD=$BUSYBOX_PATH/head
+VTOY_DM_PATH=/dev/mapper/ventoy
+VTOY_DEBUG_LEVEL=$($BUSYBOX_PATH/hexdump -n 1 -s 430 -e '1/1 "%02x"' $VTOY_PATH/ventoy_os_param)
+
+if [ "$VTOY_DEBUG_LEVEL" = "01" ]; then
+    if [ -e /dev/console ]; then
+        VTLOG=/dev/console
+    fi
+fi
+
+vtlog() {
+    if [ "$VTLOG" = "$VTOY_PATH/log" ]; then
+        echo "$*" >>$VTLOG
+    else
+        echo -e "\033[32m $* \033[0m" > $VTLOG
+        $SLEEP 2
+    fi
+}
+
+vterr() {
+    if [ "$VTLOG" = "$VTOY_PATH/log" ]; then
+        echo "$*" >>$VTLOG
+    else
+        echo -e "\n\033[31m $* \033[0m" > $VTLOG
+        $SLEEP 30
+    fi
+}
+
+
+is_ventoy_hook_finished() {
+       [ -e $VTOY_PATH/hook_finish ]
+}
+
+set_ventoy_hook_finish() {
+       echo 'Y' > $VTOY_PATH/hook_finish
+}
+
+get_ventoy_disk_name() {       
+    line=$($VTOY_PATH/tool/vtoydump -f /ventoy/ventoy_os_param)
+    if [ $? -eq 0 ]; then
+        echo ${line%%#*}
+    else    
+        echo "unknown"
+    fi
+}
+
+get_ventoy_iso_name() {
+       line=$($VTOY_PATH/tool/vtoydump -f /ventoy/ventoy_os_param)
+    if [ $? -eq 0 ]; then
+        echo ${line##*#}
+    else    
+        echo "unknown"
+    fi
+}
+
+wait_for_usb_disk_ready() {
+       while [ -n "Y" ]; do
+               usb_disk=$(get_ventoy_disk_name)
+        vtlog "wait_for_usb_disk_ready $usb_disk ..."
+        
+               if [ -e "${usb_disk}2" ]; then
+            vtlog "wait_for_usb_disk_ready $usb_disk finish"
+                       break
+               else
+                       $SLEEP 0.3
+               fi
+       done
+}
+
+is_ventoy_disk() {
+    if $VTOY_PATH/tool/vtoydump -f $VTOY_PATH/ventoy_os_param -c "$1"; then
+        $BUSYBOX_PATH/true
+    else
+        $BUSYBOX_PATH/false
+    fi
+}
+
+not_ventoy_disk() {
+    if $VTOY_PATH/tool/vtoydump -f $VTOY_PATH/ventoy_os_param -c "$1"; then
+        $BUSYBOX_PATH/false
+    else
+        $BUSYBOX_PATH/true
+    fi
+}
+
+ventoy_get_vblade_bin() {
+    if $VTOY_PATH/tool/vblade_64 -t >>$VTLOG 2>&1; then
+        echo $VTOY_PATH/tool/vblade_64
+    else
+        echo $VTOY_PATH/tool/vblade_32
+    fi
+}
+
+ventoy_find_bin_path() {
+    if $BUSYBOX_PATH/which "$1" > /dev/null; then
+        $BUSYBOX_PATH/which "$1"; return
+    fi
+    
+    for vt_path in '/bin' '/sbin' '/usr/bin' '/usr/sbin' '/usr/local/bin' '/usr/local/sbin' '/root/bin'; do
+        if [ -e "$vt_path/$1" ]; then
+            echo "$vt_path/$1"; return
+        fi
+    done
+    
+    echo ""
+}
+
+
+ventoy_find_bin_run() {    
+    vtsudo=0
+    if [ "$1" = "sudo" ]; then
+        shift
+        vtsudo=1
+    fi
+    
+    vtbinpath=$(ventoy_find_bin_path "$1")
+    if [ -n "$vtbinpath" ]; then
+        shift
+        
+        if [ $vtsudo -eq 0 ]; then
+            vtlog "$vtbinpath $*"
+            $vtbinpath $*
+        else
+            vtlog "sudo $vtbinpath $*"
+            sudo $vtbinpath $*
+        fi
+    fi
+}
+
+ventoy_get_module_postfix() {
+    vtKerVer=$($BUSYBOX_PATH/uname -r)
+    vtLine=$($FIND /lib/modules/$vtKerVer/ -name *.ko* | $HEAD -n1)
+    vtComp=${vtLine##*/*.ko}
+    echo "ko$vtComp"
+}
+
+ventoy_check_dm_module() {
+    if $GREP -q 'device-mapper' /proc/devices; then
+        $BUSYBOX_PATH/true; return
+    fi
+    
+    vtlog "device-mapper NOT found in /proc/devices, try to load kernel module"
+    $BUSYBOX_PATH/modprobe dm_mod >>$VTLOG 2>&1
+    $BUSYBOX_PATH/modprobe dm-mod >>$VTLOG 2>&1
+    
+    if ! $GREP -q 'device-mapper' /proc/devices; then
+        vtlog "modprobe failed, now try to insmod ko..."
+    
+        $FIND /lib/modules/ -name "dm-mod.ko*" | while read vtline; do
+            vtlog "insmode $vtline "
+            $BUSYBOX_PATH/insmod $vtline >>$VTLOG 2>&1
+        done
+    fi
+    
+    if $GREP -q 'device-mapper' /proc/devices; then
+        vtlog "device-mapper found in /proc/devices after retry"
+        $BUSYBOX_PATH/true; return
+    else
+        vtlog "device-mapper still NOT found in /proc/devices after retry"
+        $BUSYBOX_PATH/false; return
+    fi
+}
+
+create_ventoy_device_mapper() {
+    vtlog "create_ventoy_device_mapper $*"
+    
+    VT_DM_BIN=$(ventoy_find_bin_path dmsetup)
+    if [ -z "$VT_DM_BIN" ]; then
+        vtlog "no dmsetup avaliable, lastly try inbox dmsetup"
+        VT_DM_BIN=$VTOY_PATH/tool/dmsetup
+    fi
+    
+    vtlog "dmsetup avaliable in system $VT_DM_BIN"
+        
+    if ventoy_check_dm_module "$1"; then
+        vtlog "device-mapper module check success"
+    else
+        vterr "Error: no dm module avaliable"
+    fi
+    
+    $VTOY_PATH/tool/vtoydm -p -f $VTOY_PATH/ventoy_image_map -d $1 > $VTOY_PATH/ventoy_dm_table        
+    if [ -z "$2" ]; then
+        $VT_DM_BIN create ventoy $VTOY_PATH/ventoy_dm_table >>$VTLOG 2>&1
+    else
+        $VT_DM_BIN "$2" create ventoy $VTOY_PATH/ventoy_dm_table >>$VTLOG 2>&1
+    fi    
+}
+
+wait_for_ventoy_dm_disk_label() {
+    DM=$($BUSYBOX_PATH/readlink $VTOY_DM_PATH)    
+    vtlog "wait_for_ventoy_dm_disk_label $DM ..."
+    
+    for i in 0 1 2 3 4 5 6 7 8 9; do
+        vtlog "i=$i ####### ls /dev/disk/by-label/"
+        ls -l /dev/disk/by-label/ >> $VTLOG
+        
+        if ls -l /dev/disk/by-label/ | $GREP -q "$DM"; then
+            break
+        else
+            $SLEEP 0.3
+        fi
+    done
+}
+
+install_udeb_pkg() {
+    if ! [ -e "$1" ]; then
+        $BUSYBOX_PATH/false
+        return
+    fi
+    
+    if [ -d /tmp/vtoy_udeb ]; then
+        $BUSYBOX_PATH/rm -rf /tmp/vtoy_udeb
+    fi
+    
+    $BUSYBOX_PATH/mkdir -p /tmp/vtoy_udeb
+    $BUSYBOX_PATH/cp -a "$1" /tmp/vtoy_udeb/
+    
+    CURDIR=$($BUSYBOX_PATH/pwd)
+    cd /tmp/vtoy_udeb
+    
+    $BUSYBOX_PATH/ar x "$1"
+    
+    if [ -e 'data.tar.gz' ]; then
+        $BUSYBOX_PATH/tar -xzf data.tar.gz -C /
+    elif [ -e 'data.tar.xz' ]; then
+        $BUSYBOX_PATH/tar -xJf data.tar.xz -C /
+    elif [ -e 'data.tar.bz2' ]; then
+        $BUSYBOX_PATH/tar -xjf data.tar.bz2 -C /
+    elif [ -e 'data.tar.lzma' ]; then
+        $BUSYBOX_PATH/tar -xaf data.tar.lzma -C /
+    fi
+    
+    if [ -e 'control.tar.gz' ]; then
+        $BUSYBOX_PATH/tar -xzf control.tar.gz -C /
+    elif [ -e 'control.tar.xz' ]; then
+        $BUSYBOX_PATH/tar -xJf control.tar.xz -C /
+    elif [ -e 'control.tar.bz2' ]; then
+        $BUSYBOX_PATH/tar -xjf control.tar.bz2 -C /
+    elif [ -e 'control.tar.lzma' ]; then
+        $BUSYBOX_PATH/tar -xaf control.tar.lzma -C /
+    fi
+    
+    cd $CURDIR
+    $BUSYBOX_PATH/rm -rf /tmp/vtoy_udeb
+    $BUSYBOX_PATH/true
+}
+
+
+install_udeb_from_line() {
+    vtlog "install_udeb_from_line $1"
+
+    if ! [ -b "$2" ]; then
+        vterr "disk #$2# not exist"
+        return 
+    fi
+
+    sector=$(echo $1 | $AWK '{print $(NF-1)}')
+    length=$(echo $1 | $AWK '{print $NF}')
+    vtlog "sector=$sector  length=$length"
+    
+    $VTOY_PATH/tool/vtoydm -e -f $VTOY_PATH/ventoy_image_map -d ${2} -s $sector -l $length -o /tmp/xxx.udeb
+    if [ -e /tmp/xxx.udeb ]; then
+        vtlog "extract udeb file from iso success"
+    else
+        vterr "extract udeb file from iso fail"
+        return
+    fi
+    
+    install_udeb_pkg /tmp/xxx.udeb
+    $BUSYBOX_PATH/rm -f /tmp/xxx.udeb
+}
+
+extract_file_from_line() {
+    vtlog "extract_file_from_line $1 disk=#$2#"
+    if ! [ -b "$2" ]; then
+        vterr "disk #$2# not exist"
+        return 
+    fi
+
+    sector=$(echo $1 | $AWK '{print $(NF-1)}')
+    length=$(echo $1 | $AWK '{print $NF}')
+    vtlog "sector=$sector  length=$length"
+    
+    $VTOY_PATH/tool/vtoydm -e -f $VTOY_PATH/ventoy_image_map -d ${2} -s $sector -l $length -o $3
+    if [ -e $3 ]; then
+        vtlog "extract file from iso success"
+        $BUSYBOX_PATH/true
+    else
+        vterr "extract file from iso fail"
+        $BUSYBOX_PATH/false
+    fi
+}
+
+install_rpm_from_line() {
+    vtlog "install_rpm_from_line $1 disk=#$2#"
+
+    if ! [ -b "$2" ]; then
+        vterr "disk #$2# not exist"
+        return 
+    fi
+
+    sector=$(echo $1 | $AWK '{print $(NF-1)}')
+    length=$(echo $1 | $AWK '{print $NF}')
+    vtlog "sector=$sector  length=$length"
+    
+    $VTOY_PATH/tool/vtoydm -e -f $VTOY_PATH/ventoy_image_map -d ${2} -s $sector -l $length -o /tmp/xxx.rpm
+    if [ -e /tmp/xxx.rpm ]; then
+        vtlog "extract rpm file from iso success"
+    else
+        vterr "extract rpm file from iso fail"
+        return
+    fi
+    
+    CURPWD=$($BUSYBOX_PATH/pwd)
+    
+    cd /
+    vtlog "install rpm..."
+    $BUSYBOX_PATH/rpm2cpio /tmp/xxx.rpm | $BUSYBOX_PATH/cpio -idm 2>>$VTLOG
+    cd $CURPWD
+    
+    $BUSYBOX_PATH/rm -f /tmp/xxx.rpm
+}
+
+dump_whole_iso_file() {
+   $VTOY_PATH/tool/vtoydm -p -f $VTOY_PATH/ventoy_image_map -d $usb_disk | while read vtline; do
+        vtlog "dmtable line: $vtline"
+        vtcount=$(echo $vtline | $AWK '{print $2}')
+        vtoffset=$(echo $vtline | $AWK '{print $NF}')
+        $BUSYBOX_PATH/dd if=$usb_disk of="$1" bs=512 count=$vtcount skip=$vtoffset oflag=append conv=notrunc 
+    done 
+}
+
+ventoy_copy_device_mapper() {
+    if [ -L $VTOY_DM_PATH ]; then
+        vtlog "replace block device link $1..."
+        $BUSYBOX_PATH/mv "$1" $VTOY_PATH/dev_backup_${1#/dev/}
+        VT_MAPPER_LINK=$($BUSYBOX_PATH/readlink $VTOY_DM_PATH)
+        $BUSYBOX_PATH/cp -a "/dev/mapper/$VT_MAPPER_LINK" "$1"
+    elif [ -b $VTOY_DM_PATH ]; then
+        vtlog "replace block device $1..."
+        $BUSYBOX_PATH/mv "$1" $VTOY_PATH/dev_backup_${1#/dev/}            
+        $BUSYBOX_PATH/cp -a "$VTOY_DM_PATH" "$1"
+    else
+    
+        vtlog "$VTOY_DM_PATH not exist, now check /dev/dm-X ..."
+        VT_DM_BIN=$(ventoy_find_bin_path dmsetup)
+        if [ -z "$VT_DM_BIN" ]; then
+            vtlog "no dmsetup avaliable, lastly try inbox dmsetup"
+            VT_DM_BIN=$VTOY_PATH/tool/dmsetup
+        fi
+    
+        DM_VT_ID=$($VT_DM_BIN ls | $GREP ventoy | $SED 's/.*(\([0-9][0-9]*\),.*\([0-9][0-9]*\).*/\1 \2/')
+        vtlog "DM_VT_ID=$DM_VT_ID ..."
+        $BUSYBOX_PATH/mv "$1" $VTOY_PATH/dev_backup_${1#/dev/}            
+        $BUSYBOX_PATH/mknod -m 0666 "$1" b $DM_VT_ID
+    fi 
+}
+
+ventoy_udev_disk_common_hook() {
+    
+    VTDISK="${1:0:-1}"
+    
+    # create device mapper for iso image file
+    if create_ventoy_device_mapper "/dev/$VTDISK" --readonly; then
+        vtlog "==== create ventoy device mapper success ===="
+    else
+        vtlog "==== create ventoy device mapper failed ===="
+        
+        $SLEEP 5
+        
+        if $GREP -q "/dev/$VTDISK" /proc/mounts; then
+            $GREP "/dev/$VTDISK" /proc/mounts | while read vtLine; do
+                vtPart=$(echo $vtLine | $AWK '{print $1}')
+                vtMnt=$(echo $vtLine | $AWK '{print $2}')
+                vtlog "$vtPart is mounted on $vtMnt  now umount it ..."
+                $BUSYBOX_PATH/umount $vtMnt
+            done
+        fi
+        
+        if create_ventoy_device_mapper "/dev/$VTDISK" --readonly; then
+            vtlog "==== create ventoy device mapper success after retry ===="
+        else
+            vtlog "==== create ventoy device mapper failed after retry ===="
+            return
+        fi
+    fi
+    
+    if [ "$2" = "noreplace" ]; then
+        vtlog "no need to replace block device"
+    else
+        ventoy_copy_device_mapper "/dev/$1"
+    fi
+}
+
+
diff --git a/IMG/cpio/ventoy/hook/ventoy-os-lib.sh b/IMG/cpio/ventoy/hook/ventoy-os-lib.sh
new file mode 100644 (file)
index 0000000..0be5a0a
--- /dev/null
@@ -0,0 +1,98 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+VT_RULE_DIR_PREFIX=""
+VT_PRINTK_LEVEL=0
+VT_UDEV_RULE_FILE_NAME="99-ventoy.rules"
+VT_UDEV_RULE_PREFIX="ACTION==\"add\", SUBSYSTEM==\"block\","
+
+ventoy_close_printk() {
+    VT_PRINTK_LEVEL=$($CAT /proc/sys/kernel/printk | $AWK '{print $1}')
+    if [ -e /proc/sys/kernel/printk ]; then
+        echo 0 > /proc/sys/kernel/printk
+    fi
+}
+
+ventoy_restore_printk() {
+    if [ -e /proc/sys/kernel/printk ]; then
+        echo $VT_PRINTK_LEVEL > /proc/sys/kernel/printk
+    fi
+}
+
+ventoy_set_rule_dir_prefix() {
+    VT_RULE_DIR_PREFIX=$1
+}
+
+ventoy_get_udev_conf_dir() {
+    if [ -d $VT_RULE_DIR_PREFIX/etc/udev/rules.d ]; then
+        VT_RULE_PATH=$VT_RULE_DIR_PREFIX/etc/udev/rules.d
+    elif [ -d $VT_RULE_DIR_PREFIX/lib/udev/rules.d ]; then
+        VT_RULE_PATH=$VT_RULE_DIR_PREFIX/lib/udev/rules.d
+    else
+        $BUSYBOX_PATH/mkdir -p $VT_RULE_DIR_PREFIX/etc/udev/rules.d
+        VT_RULE_PATH=$VT_RULE_DIR_PREFIX/etc/udev/rules.d
+    fi
+    echo -n "$VT_RULE_PATH"
+}
+
+ventoy_get_udev_conf_path() {
+    VT_RULE_DIR=$(ventoy_get_udev_conf_dir)
+    echo "$VT_RULE_DIR/$VT_UDEV_RULE_FILE_NAME"
+}
+
+ventoy_add_kernel_udev_rule() {
+    VT_UDEV_RULE_PATH=$(ventoy_get_udev_conf_path)
+    echo "KERNEL==\"$1\", $VT_UDEV_RULE_PREFIX RUN+=\"$2\"" >> $VT_UDEV_RULE_PATH
+}
+
+ventoy_add_udev_rule_with_name() {
+    VT_UDEV_RULE_DIR=$(ventoy_get_udev_conf_dir)
+    echo "KERNEL==\"*2\", $VT_UDEV_RULE_PREFIX RUN+=\"$1\"" >> $VT_UDEV_RULE_DIR/$2
+}
+
+ventoy_add_udev_rule_with_path() {
+    echo "KERNEL==\"*2\", $VT_UDEV_RULE_PREFIX RUN+=\"$1\"" >> $2
+}
+
+ventoy_add_udev_rule() {
+    VT_UDEV_RULE_PATH=$(ventoy_get_udev_conf_path)
+    echo "KERNEL==\"*2\", $VT_UDEV_RULE_PREFIX RUN+=\"$1\"" >> $VT_UDEV_RULE_PATH
+}
+
+#
+# It seems there is a bug in somw version of systemd-udevd
+# https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=869719
+#
+ventoy_systemd_udevd_work_around() {
+    VTSYSTEMUDEV="$VT_RULE_DIR_PREFIX/lib/systemd/system/systemd-udevd.service"
+    if [ -e $VTSYSTEMUDEV ]; then
+        if $GREP -q 'SystemCallArchitectures.*native' $VTSYSTEMUDEV; then
+            $SED "s/.*\(SystemCallArchitectures.*native\)/#\1/g"  -i $VTSYSTEMUDEV
+        fi
+    fi
+}
+
+ventoy_print_yum_repo() {
+    echo "[$1]"
+    echo "name=$1"
+    echo "baseurl=$2"
+    echo "enabled=1"
+    echo "gpgcheck=0"
+    echo "priority=0"
+}
diff --git a/IMG/cpio/ventoy/hook/xen/udev_disk_hook.sh b/IMG/cpio/ventoy/hook/xen/udev_disk_hook.sh
new file mode 100644 (file)
index 0000000..b25c1fb
--- /dev/null
@@ -0,0 +1,32 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. /ventoy/hook/ventoy-hook-lib.sh
+
+if is_ventoy_hook_finished || not_ventoy_disk "${1:0:-1}"; then
+    exit 0
+fi
+
+ventoy_udev_disk_common_hook $*
+
+# trick for xen6
+ventoy_copy_device_mapper /dev/sr7
+
+# OK finish
+set_ventoy_hook_finish
diff --git a/IMG/cpio/ventoy/hook/xen/ventoy-hook.sh b/IMG/cpio/ventoy/hook/xen/ventoy-hook.sh
new file mode 100644 (file)
index 0000000..1260ce1
--- /dev/null
@@ -0,0 +1,24 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+. $VTOY_PATH/hook/ventoy-os-lib.sh
+
+ventoy_systemd_udevd_work_around
+
+ventoy_add_udev_rule "$VTOY_PATH/hook/xen/udev_disk_hook.sh %k"
diff --git a/IMG/cpio/ventoy/init b/IMG/cpio/ventoy/init
new file mode 100644 (file)
index 0000000..bfb7adf
--- /dev/null
@@ -0,0 +1,154 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+
+###################################################################
+#                                                                  #
+# Step 1 : parse kernel debug parameter                            #
+#                                                                  #
+####################################################################
+mkdir /proc; mount -t proc proc /proc
+vtcmdline=$(cat /proc/cmdline)
+vtkerver=$(cat /proc/version)
+umount /proc; rm -rf /proc
+
+echo "kenel version=$vtkerver" >>$VTLOG
+echo "kenel cmdline=$vtcmdline" >>$VTLOG
+
+#break here for debug
+if [ "$VTOY_BREAK_LEVEL" = "01" ] || [ "$VTOY_BREAK_LEVEL" = "11" ]; then
+    sleep 5
+    echo -e "\n\n\033[32m ################################################# \033[0m"
+    echo -e "\033[32m ################ VENTOY DEBUG ################### \033[0m"
+    echo -e "\033[32m ################################################# \033[0m \n"
+    
+    if [ "$VTOY_BREAK_LEVEL" = "11" ]; then
+        cat $VTLOG
+    fi
+    exec $BUSYBOX_PATH/sh
+fi
+
+
+####################################################################
+#                                                                  #
+# Step 2 : extract real initramfs to /                             #
+#                                                                  #
+####################################################################
+cd /
+rm -rf /init /linuxrc /sbin /dev/  /root
+
+ventoy_is_initrd_ramdisk() {
+    #As I known, PCLinuxOS use ramdisk
+    if echo $vtkerver | grep -i -q 'PCLinuxOS'; then
+        true
+    else
+        false
+    fi
+}
+
+# param: file skip magic tmp
+ventoy_unpack_initramfs() {        
+    vtfile=$1; vtskip=$2; vtmagic=$3; vttmp=$4    
+    echo "=====ventoy_unpack_initramfs: #$*#" >> $VTLOG
+    
+    for vtx in '1F8B zcat' '1F9E zcat' '425A bzcat' '5D00 lzcat' 'FD37 xzcat' '894C lzopcat' '0221 lz4cat' '28B5 zstdcat' '3037 cat'; do
+        if [ "${vtx:0:4}" = "${vtmagic:0:4}" ]; then
+            echo "vtx=$vtx" >> $VTLOG            
+            if [ $vtskip -eq 0 ]; then
+                ${vtx:5} $vtfile | (cpio -idm 2>>$VTLOG; cat > $vttmp)
+            else
+                dd if=$vtfile skip=$vtskip iflag=skip_bytes status=none | ${vtx:5} | (cpio -idm 2>>$VTLOG; cat > $vttmp)
+            fi
+            break
+        fi
+    done
+}
+
+# param: file magic tmp
+ventoy_unpack_initrd() {        
+    vtfile=$1; vtmagic=$2; vttmp=$3    
+    echo "=====ventoy_unpack_initrd: #$*#" >> $VTLOG
+    
+    for vtx in '1F8B zcat' '1F9E zcat' '425A bzcat' '5D00 lzcat' 'FD37 xzcat' '894C lzopcat' '0221 lz4cat' '28B5 zstdcat' '3037 cat'; do
+        if [ "${vtx:0:4}" = "${vtmagic:0:4}" ]; then
+            echo "vtx=$vtx" >> $VTLOG            
+            ${vtx:5} $vtfile > $vttmp
+            break
+        fi
+    done    
+}
+
+
+# This export is for busybox cpio command
+export EXTRACT_UNSAFE_SYMLINKS=1
+
+for vtfile in $(ls /initrd*); do    
+    #decompress first initrd
+    vtmagic=$(hexdump -n 2 -e '2/1 "%02X"' $vtfile)
+
+    if ventoy_is_initrd_ramdisk; then
+        ventoy_unpack_initrd $vtfile $vtmagic ${vtfile}_tmp
+        mv ${vtfile}_tmp $vtfile
+        break
+    else
+        ventoy_unpack_initramfs $vtfile 0 $vtmagic ${vtfile}_tmp
+    fi
+
+    #only for cpio,cpio,...,initrd sequence, initrd,cpio or initrd,initrd sequence is not supported
+    while [ -e ${vtfile}_tmp ] && [ $(stat -c '%s' ${vtfile}_tmp) -gt 512 ]; do
+        mv ${vtfile}_tmp $vtfile
+        vtdump=$(hexdump -n 512 -e '512/1 "%02X"' $vtfile)
+        vtmagic=$(echo $vtdump | sed 's/^\(00\)*//')
+        let vtoffset="(${#vtdump}-${#vtmagic})/2"
+        
+        if [ -z "$vtmagic" ]; then
+            echo "terminate with all zero data file" >> $VTLOG
+            break
+        fi
+        
+        ventoy_unpack_initramfs $vtfile $vtoffset ${vtmagic:0:4} ${vtfile}_tmp
+    done
+    
+    rm -f $vtfile ${vtfile}_tmp
+done
+
+
+#break here for debug
+if [ "$VTOY_BREAK_LEVEL" = "02" ] || [ "$VTOY_BREAK_LEVEL" = "12" ]; then
+    sleep 5
+    echo -e "\n\n\033[32m ################################################# \033[0m"
+    echo -e "\033[32m ################ VENTOY DEBUG ################### \033[0m"
+    echo -e "\033[32m ################################################# \033[0m \n"   
+    if [ "$VTOY_BREAK_LEVEL" = "12" ]; then 
+        cat $VTOY_PATH/log
+    fi    
+    exec $BUSYBOX_PATH/sh
+fi
+
+
+####################################################################
+#                                                                  #
+# Step 3 : Hand over to ventoy.sh                                  #
+#                                                                  #
+####################################################################
+echo "Now hand over to ventoy.sh" >>$VTLOG
+. $VTOY_PATH/tool/vtoytool_install.sh
+
+export PATH=$VTOY_ORG_PATH
+exec $BUSYBOX_PATH/sh $VTOY_PATH/ventoy.sh
diff --git a/IMG/cpio/ventoy/tool/ar b/IMG/cpio/ventoy/tool/ar
new file mode 100644 (file)
index 0000000..fd443b2
Binary files /dev/null and b/IMG/cpio/ventoy/tool/ar differ
diff --git a/IMG/cpio/ventoy/tool/inotifyd b/IMG/cpio/ventoy/tool/inotifyd
new file mode 100644 (file)
index 0000000..29c70d2
Binary files /dev/null and b/IMG/cpio/ventoy/tool/inotifyd differ
diff --git a/IMG/cpio/ventoy/tool/lz4cat b/IMG/cpio/ventoy/tool/lz4cat
new file mode 100644 (file)
index 0000000..bccad01
Binary files /dev/null and b/IMG/cpio/ventoy/tool/lz4cat differ
diff --git a/IMG/cpio/ventoy/tool/ventoy_loader.sh b/IMG/cpio/ventoy/tool/ventoy_loader.sh
new file mode 100644 (file)
index 0000000..5844cbd
--- /dev/null
@@ -0,0 +1,20 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+exec /ventoy/busybox/sh
diff --git a/IMG/cpio/ventoy/tool/vtoytool_install.sh b/IMG/cpio/ventoy/tool/vtoytool_install.sh
new file mode 100644 (file)
index 0000000..c408d01
--- /dev/null
@@ -0,0 +1,54 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+echo "#### install vtoytool #####" >> $VTLOG
+
+if ! [ -e $BUSYBOX_PATH/ar ]; then
+    $BUSYBOX_PATH/ln -s $VTOY_PATH/tool/ar $BUSYBOX_PATH/ar
+fi
+
+for vtdir in $(ls $VTOY_PATH/tool/vtoytool/); do
+    echo "try $VTOY_PATH/tool/vtoytool/$vtdir/ ..." >> $VTLOG
+    if $VTOY_PATH/tool/vtoytool/$vtdir/vtoytool_64 --install 2>>$VTLOG; then
+        echo "vtoytool_64 OK" >> $VTLOG
+        break
+    fi
+    
+    if $VTOY_PATH/tool/vtoytool/$vtdir/vtoytool_32 --install 2>>$VTLOG; then
+        echo "vtoytool_32 OK" >> $VTLOG
+        break
+    fi
+done
+
+if $VTOY_PATH/tool/vtoy_fuse_iso_64 -t 2>>$VTLOG; then
+    echo "use vtoy_fuse_iso_64" >>$VTLOG
+    $BUSYBOX_PATH/cp -a $VTOY_PATH/tool/vtoy_fuse_iso_64  $VTOY_PATH/tool/vtoy_fuse_iso
+else
+    echo "use vtoy_fuse_iso_32" >>$VTLOG    
+    $BUSYBOX_PATH/cp -a $VTOY_PATH/tool/vtoy_fuse_iso_32 $VTOY_PATH/tool/vtoy_fuse_iso
+fi
+
+if $VTOY_PATH/tool/unsquashfs_64 -t 2>>$VTLOG; then
+    echo "use unsquashfs_64" >>$VTLOG
+    $BUSYBOX_PATH/cp -a $VTOY_PATH/tool/unsquashfs_64  $VTOY_PATH/tool/vtoy_unsquashfs
+else
+    echo "use unsquashfs_32" >>$VTLOG    
+    $BUSYBOX_PATH/cp -a $VTOY_PATH/tool/unsquashfs_32 $VTOY_PATH/tool/vtoy_unsquashfs
+fi
+
diff --git a/IMG/cpio/ventoy/tool/zstdcat b/IMG/cpio/ventoy/tool/zstdcat
new file mode 100644 (file)
index 0000000..e8657c6
Binary files /dev/null and b/IMG/cpio/ventoy/tool/zstdcat differ
diff --git a/IMG/cpio/ventoy/ventoy.sh b/IMG/cpio/ventoy/ventoy.sh
new file mode 100644 (file)
index 0000000..6426dc8
--- /dev/null
@@ -0,0 +1,203 @@
+#!/ventoy/busybox/sh
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+####################################################################
+#                                                                  #
+# Step 1 : Parse kernel parameter                                  #
+#                                                                  #
+####################################################################
+if ! [ -e /proc ]; then
+    $BUSYBOX_PATH/mkdir /proc
+    rmproc='Y'
+fi
+$BUSYBOX_PATH/mount -t proc proc /proc
+
+# vtinit=xxx to replace rdinit=xxx
+vtcmdline=$($CAT /proc/cmdline)
+for i in $vtcmdline; do
+    if echo $i | $GREP -q vtinit; then
+        user_rdinit=${i#vtinit=}
+        echo "user set user_rdinit=${user_rdinit}" >>$VTLOG
+    fi
+done
+
+
+####################################################################
+#                                                                  #
+# Step 2 : Do OS specific hook                                     #
+#                                                                  #
+####################################################################
+ventoy_get_os_type() {
+    echo "kernel version" >> $VTLOG
+    $CAT /proc/version >> $VTLOG
+
+    # rhel5/CentOS5 and all other distributions based on them
+    if $GREP -q 'el5' /proc/version; then
+        echo 'rhel5'; return
+
+    # rhel6/CentOS6 and all other distributions based on them
+    elif $GREP -q 'el6' /proc/version; then
+        echo 'rhel6'; return
+
+    # rhel7/CentOS7/rhel8/CentOS8 and all other distributions based on them
+    elif $GREP -q 'el[78]' /proc/version; then
+        echo 'rhel7'; return   
+
+    # Maybe rhel9 rhel1x use the same way? Who knows!
+    elif $EGREP -q 'el9|el1[0-9]' /proc/version; then
+        echo 'rhel7'; return   
+        
+    # Fedora : do the same process with rhel7
+    elif $GREP -q '\.fc[0-9][0-9]\.' /proc/version; then
+        echo 'rhel7'; return
+        
+    # Debian :
+    elif $GREP -q '[Dd]ebian' /proc/version; then
+        echo 'debian'; return
+        
+    # Ubuntu : do the same process with debian
+    elif $GREP -q '[Uu]buntu' /proc/version; then
+        echo 'debian'; return
+        
+    # Deepin : do the same process with debian
+    elif $GREP -q '[Dd]eepin' /proc/version; then
+        echo 'debian'; return
+        
+    # SUSE
+    elif $GREP -q 'SUSE' /proc/version; then
+        echo 'suse'; return
+        
+    # ArchLinux
+    elif $EGREP -q 'archlinux|ARCH' /proc/version; then
+        echo 'arch'; return
+    
+    # gentoo
+    elif $EGREP -q '[Gg]entoo' /proc/version; then
+        echo 'gentoo'; return
+        
+    # TinyCore
+    elif $EGREP -q 'tinycore' /proc/version; then
+        echo 'tinycore'; return
+    
+    # manjaro
+    elif $EGREP -q 'manjaro|MANJARO' /proc/version; then
+        echo 'manjaro'; return
+        
+    # mageia
+    elif $EGREP -q 'mageia' /proc/version; then
+        echo 'mageia'; return
+    
+    # pclinux OS
+    elif $GREP -i -q 'PCLinuxOS' /proc/version; then
+        echo 'pclos'; return
+    
+    # KaOS
+    elif $GREP -i -q 'kaos' /proc/version; then
+        echo 'kaos'; return
+    
+    # Alpine
+    elif $GREP -q 'Alpine' /proc/version; then
+        echo 'alpine'; return
+
+    # NixOS
+    elif $GREP -i -q 'NixOS' /proc/version; then
+        echo 'nixos'; return
+    
+    fi
+
+    if [ -e /lib/debian-installer ]; then
+        echo 'debian'; return
+    fi
+
+    if [ -e /etc/os-release ]; then
+        if $GREP -q 'XenServer' /etc/os-release; then
+            echo 'xen'; return
+        elif $GREP -q 'SUSE ' /etc/os-release; then
+            echo 'suse'; return
+        fi
+    fi
+    
+    if $BUSYBOX_PATH/dmesg | $GREP -q -m1 "Xen:"; then
+        echo 'xen'; return
+    fi
+    
+    
+    if [ -e /etc/HOSTNAME ] && $GREP -i -q 'slackware' /etc/HOSTNAME; then
+        echo 'slackware'; return
+    fi
+    
+    
+    echo "default"
+}
+
+VTOS=$(ventoy_get_os_type)
+echo "OS=###${VTOS}###" >>$VTLOG
+if [ -e "$VTOY_PATH/hook/$VTOS/ventoy-hook.sh" ]; then
+    $BUSYBOX_PATH/sh "$VTOY_PATH/hook/$VTOS/ventoy-hook.sh"
+fi
+
+
+####################################################################
+#                                                                  #
+# Step 3 : Check for debug break                                   #
+#                                                                  #
+####################################################################
+if [ "$VTOY_BREAK_LEVEL" = "03" ] || [ "$VTOY_BREAK_LEVEL" = "13" ]; then
+    $SLEEP 5
+    echo -e "\n\n\033[32m ################################################# \033[0m"
+    echo -e "\033[32m ################ VENTOY DEBUG ################### \033[0m"
+    echo -e "\033[32m ################################################# \033[0m \n"
+    if [ "$VTOY_BREAK_LEVEL" = "13" ]; then 
+        $CAT $VTOY_PATH/log
+    fi
+    exec $BUSYBOX_PATH/sh
+fi
+
+
+
+####################################################################
+#                                                                  #
+# Step 4 : Hand over to real init                                  #
+#                                                                  #
+####################################################################
+$BUSYBOX_PATH/umount /proc
+if [ "$rmproc" = "Y" ]; then
+    $BUSYBOX_PATH/rm -rf /proc
+fi
+
+cd /
+unset VTOY_PATH VTLOG FIND GREP EGREP CAT AWK SED SLEEP HEAD
+
+for vtinit in $user_rdinit /init /sbin/init /linuxrc; do
+    if [ -d /ventoy_rdroot ]; then
+        if [ -e "/ventoy_rdroot$vtinit" ]; then
+            # switch_root will check /init file, this is a cheat code
+            echo 'switch_root' > /init
+            exec $BUSYBOX_PATH/switch_root /ventoy_rdroot "$vtinit"
+        fi
+    else
+        if [ -e "$vtinit" ];then
+            exec "$vtinit"
+        fi
+    fi
+done
+
+# Should never reach here
+echo -e "\n\n\033[31m ############ INIT NOT FOUND ############### \033[0m \n"
+exec $BUSYBOX_PATH/sh
diff --git a/IMG/mkcpio.sh b/IMG/mkcpio.sh
new file mode 100644 (file)
index 0000000..517b55c
--- /dev/null
@@ -0,0 +1,40 @@
+#!/bin/bash
+
+VENTOY_PATH=$PWD/../
+
+rm -f ventoy.cpio
+
+chmod -R 777 cpio
+
+cp -a cpio     cpio_tmp
+
+cd cpio_tmp
+rm -f init
+ln -s sbin/init init
+ln -s sbin/init linuxrc
+
+cd ventoy
+
+
+
+find ./tool | cpio  -o -H newc>tool.cpio
+xz tool.cpio
+rm -rf tool
+
+xz ventoy.sh
+
+find ./hook | cpio  -o -H newc>hook.cpio
+xz hook.cpio
+rm -rf hook
+cd ..
+
+find .| cpio  -o -H newc>../ventoy.cpio
+
+cd ..
+rm -rf cpio_tmp
+
+echo '======== SUCCESS ============='
+
+rm -f $VENTOY_PATH/INSTALL/ventoy/ventoy.cpio
+cp -a ventoy.cpio $VENTOY_PATH/INSTALL/ventoy/
+
diff --git a/INSTALL/EFI/BOOT/BOOTX64.EFI b/INSTALL/EFI/BOOT/BOOTX64.EFI
new file mode 100644 (file)
index 0000000..9d5c2c9
Binary files /dev/null and b/INSTALL/EFI/BOOT/BOOTX64.EFI differ
diff --git a/INSTALL/Ventoy2Disk.exe b/INSTALL/Ventoy2Disk.exe
new file mode 100644 (file)
index 0000000..5d3c551
Binary files /dev/null and b/INSTALL/Ventoy2Disk.exe differ
diff --git a/INSTALL/Ventoy2Disk.sh b/INSTALL/Ventoy2Disk.sh
new file mode 100644 (file)
index 0000000..38cd002
--- /dev/null
@@ -0,0 +1,234 @@
+#!/bin/sh
+
+. ./tool/ventoy_lib.sh
+
+print_usage() {
+    echo 'Usage:  VentoyInstaller.sh OPTION /dev/sdX'
+    echo '  OPTION:'
+    echo '   -i  install ventoy to sdX (fail if disk already installed with ventoy)'
+    echo '   -u  update ventoy in sdX'
+    echo '   -I  force install ventoy to sdX (no matter installed or not)'
+    echo ''
+}
+
+echo ''
+echo '***********************************************************'
+echo '*                Ventoy2Disk Script                       *'
+echo '*             longpanda  admin@ventoy.net                 *'
+echo '***********************************************************'
+echo ''
+
+vtdebug "############# Ventoy2Disk ################"
+
+if ! [ -e ventoy/version ]; then
+    vterr "Please run under the correct directory!"
+    exit 1
+fi
+
+if [ "$1" = "-i" ]; then
+    MODE="install"
+elif [ "$1" = "-I" ]; then
+    MODE="install"
+    FORCE="Y"
+elif [ "$1" = "-u" ]; then
+    MODE="update"
+else
+    print_usage
+    exit 1
+fi
+
+if ! [ -b "$2" ]; then
+    print_usage
+    exit 1
+fi
+
+if [ -z "$SUDO_USER" ]; then
+    if [ "$USER" != "root" ]; then
+        vterr "EUID is $EUID root permission is required."
+        echo ''
+        exit 1
+    fi
+fi
+
+vtdebug "MODE=$MODE FORCE=$FORCE"
+
+#decompress tool
+cd tool
+chmod +x ./xzcat
+for file in $(ls); do
+    if [ "$file" != "xzcat" ]; then
+        if [ "$file" != "ventoy_lib.sh" ]; then
+            ./xzcat $file > ${file%.xz}
+            chmod +x ${file%.xz}
+        fi
+    fi
+done
+cd ../
+
+if ! check_tool_work_ok; then
+    vterr "Some tools can not run in current system. Please check log.txt for detail."
+    exit 1
+fi
+
+
+DISK=$2
+
+if ! [ -b "$DISK" ]; then
+    vterr "Disk $DISK does not exist"
+    exit 1
+fi
+
+
+if [ -e /sys/class/block/${DISK#/dev/}/start ]; then
+    vterr "$DISK is a partition, please use the whole disk"
+    exit 1
+fi
+
+if grep "$DISK" /proc/mounts; then
+    vterr "$DISK is already mounted, please umount it first!"
+    exit 1
+fi
+
+
+if [ "$MODE" = "install" ]; then
+    vtdebug "install ventoy ..."
+    
+    if ! fdisk -v >/dev/null 2>&1; then
+        vterr "fdisk is needed by ventoy installation, but is not found in the system."
+        exit 1
+    fi
+    
+    version=$(get_disk_ventoy_version $DISK)
+    if [ $? -eq 0 ]; then
+        if [ -z "$FORCE" ]; then
+            vtwarn "$DISK already contains a Ventoy with version $version"
+            vtwarn "Use -u option to do a safe upgrade operation."
+            vtwarn "OR if you really want to reinstall ventoy to $DISK, please use -I option."
+            vtwarn ""
+            exit 1
+        fi
+    fi
+    
+    disk_sector_num=$(cat /sys/block/${DISK#/dev/}/size)
+    disk_size_gb=$(expr $disk_sector_num / 2097152)
+
+    if [ $disk_sector_num -gt 4294967296 ]; then
+        vterr "$DISK is over 2TB size, MBR will not work on it."
+        exit 1
+    fi
+
+    #Print disk info
+    echo "Disk : $DISK"
+    parted $DISK p 2>&1 | grep Model
+    echo "Size : $disk_size_gb GB"
+    echo ''
+
+    vtwarn "Attention:"
+    vtwarn "You will install Ventoy to $DISK."
+    vtwarn "All the data on the disk $DISK will be lost!!!"
+    echo ""
+
+    read -p 'Continue? (y/n)'  Answer
+    if [ "$Answer" != "y" ]; then
+        if [ "$Answer" != "Y" ]; then
+            exit 0
+        fi
+    fi
+
+    echo ""
+    vtwarn "All the data on the disk $DISK will be lost!!!"
+    read -p 'Double-check. Continue? (y/n)'  Answer
+    if [ "$Answer" != "y" ]; then
+        if [ "$Answer" != "Y" ]; then
+            exit 0
+        fi
+    fi
+
+
+    if [ $disk_sector_num -le $VENTOY_SECTOR_NUM ]; then  
+        vterr "No enough space in disk $DISK"
+        exit 1
+    fi
+
+    if ! dd if=/dev/zero of=$DISK bs=1 count=512 status=none; then
+        vterr "Write data to $DISK failed, please check whether it's in use."
+        exit 1
+    fi
+
+    format_ventoy_disk $DISK
+
+    # format part1
+    if ventoy_is_linux64; then
+        cmd=./tool/mkexfatfs_64
+    else
+        cmd=./tool/mkexfatfs_32
+    fi
+
+    chmod +x ./tool/*
+
+    # DiskSize > 32GB  Cluster Size use 128KB
+    # DiskSize < 32GB  Cluster Size use 32KB
+    if [ $disk_size_gb -gt 32 ]; then
+        cluster_sectors=256
+    else
+        cluster_sectors=64
+    fi
+
+    $cmd -n ventoy -s $cluster_sectors ${DISK}1
+
+    dd status=none if=./boot/boot.img of=$DISK bs=1 count=446 
+    ./tool/xzcat ./boot/core.img.xz | dd status=none of=$DISK bs=512 count=2047 seek=1
+    ./tool/xzcat ./ventoy/ventoy.disk.img.xz | dd status=none of=$DISK bs=512 count=$VENTOY_SECTOR_NUM seek=$part2_start_sector
+
+
+    chmod +x ./tool/vtoy_gen_uuid
+    ./tool/vtoy_gen_uuid | dd status=none of=${DISK} seek=384 bs=1 count=16
+
+    sync
+
+    echo ""
+    vtinfo "Install Ventoy to $DISK successfully finished."
+    echo ""
+    
+else
+    vtdebug "update ventoy ..."
+    
+    oldver=$(get_disk_ventoy_version $DISK)
+    if [ $? -ne 0 ]; then
+        vtwarn "$DISK does not contain ventoy or data corupted"
+        echo ""
+        vtwarn "Please use -i option if you want to install ventoy to $DISK"
+        echo ""
+        exit 1
+    fi
+
+    curver=$(cat ./ventoy/version)
+
+    vtinfo "Upgrade operation is safe, all the data in the 1st partition (iso files and other) will be unchanged!"
+    echo ""
+
+    read -p "Update Ventoy  $oldver ===> $curver   Continue? (y/n)"  Answer
+    if [ "$Answer" != "y" ]; then
+        if [ "$Answer" != "Y" ]; then
+            exit 0
+        fi
+    fi
+
+    PART2=$(get_disk_part_name $DISK 2)
+    
+    dd status=none if=./boot/boot.img of=$DISK bs=1 count=446 
+    
+    ./tool/xzcat ./boot/core.img.xz | dd status=none of=$DISK bs=512 count=2047 seek=1  
+
+    disk_sector_num=$(cat /sys/block/${DISK#/dev/}/size) 
+    part2_start=$(expr $disk_sector_num - $VENTOY_SECTOR_NUM)
+    ./tool/xzcat ./ventoy/ventoy.disk.img.xz | dd status=none of=$DISK bs=512 count=$VENTOY_SECTOR_NUM seek=$part2_start
+
+    sync
+
+    echo ""
+    vtinfo "Update Ventoy to $DISK successfully finished."
+    echo ""
+    
+fi
+
diff --git a/INSTALL/grub/fonts/ascii.pf2 b/INSTALL/grub/fonts/ascii.pf2
new file mode 100644 (file)
index 0000000..1eb3edc
Binary files /dev/null and b/INSTALL/grub/fonts/ascii.pf2 differ
diff --git a/INSTALL/grub/grub.cfg b/INSTALL/grub/grub.cfg
new file mode 100644 (file)
index 0000000..6872363
--- /dev/null
@@ -0,0 +1,359 @@
+#************************************************************************************
+# Copyright (c) 2020, longpanda <admin@ventoy.net>
+# 
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License as
+# published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+# 
+#************************************************************************************
+
+function get_os_type {
+    set vtoy_os=Linux    
+    for file in "efi/microsoft" "sources/boot.wim" "boot/bcd" "bootmgr.efi" "boot/etfsboot.com"; do
+        if [ -e $1/$file ]; then
+            set vtoy_os=Windows
+            break
+        fi
+    done
+    
+    if [ -n "${vtdebug_flag}" ]; then
+        echo ISO is $vtoy_os
+    fi
+}
+
+function locate_initrd {    
+    vt_linux_locate_initrd 
+
+    if [ -n "${vtdebug_flag}" ]; then
+        vt_linux_dump_initrd
+        sleep 5
+    fi
+}
+
+function find_wim_file {
+    unset ventoy_wim_file
+    
+    for file in "sources/boot.wim" "sources/BOOT.WIM" "Sources/Win10PEx64.WIM" "boot/BOOT.WIM" "winpe_x64.wim"; do
+        if [ -e $1/$file ]; then
+            set ventoy_wim_file=$1/$file
+            break
+        fi
+    done
+}
+
+function distro_specify_initrd_file {
+    if [ -e (loop)/boot/all.rdz ]; then
+        vt_linux_specify_initrd_file /boot/all.rdz
+    elif [ -e (loop)/boot/xen.gz ]; then 
+        if [ -e (loop)/install.img ]; then
+            vt_linux_specify_initrd_file /install.img
+        fi
+    elif [ -d (loop)/casper ]; then 
+        if [ -e (loop)/casper/initrd ]; then
+            vt_linux_specify_initrd_file /casper/initrd
+        fi
+        if [ -e (loop)/casper/initrd-oem ]; then
+            vt_linux_specify_initrd_file /casper/initrd-oem
+        fi
+    fi
+}
+
+function uefi_windows_menu_func {
+    vt_windows_reset
+    
+    if [ "$ventoy_compatible" = "NO" ]; then        
+        find_wim_file (loop)
+        if [ -n "$ventoy_wim_file" ]; then
+            vt_windows_locate_wim $ventoy_wim_file
+        fi
+    fi
+    
+    vt_windows_chain_data ${1}${chosen_path}
+
+    if [ -n "$vtoy_chain_mem_addr" ]; then
+        terminal_output  console       
+        chainloader ${vtoy_path}/ventoy_x64.efi  env_param=${env_param} isoefi=${LoadIsoEfiDriver} ${vtdebug_flag} mem:${vtoy_chain_mem_addr}:size:${vtoy_chain_mem_size}
+        boot
+    else
+        echo "chain empty failed"
+        sleep 5
+    fi
+}
+
+function uefi_linux_menu_func {
+    if [ "$ventoy_compatible" = "NO" ]; then    
+        vt_load_cpio  ${vtoy_path}/ventoy.cpio
+
+        vt_linux_clear_initrd
+        
+        for file in "boot/grub/grub.cfg" "EFI/BOOT/grub.cfg" "EFI/boot/grub.cfg" "efi/boot/grub.cfg" "EFI/BOOT/BOOTX64.conf"; do
+            if [ -e (loop)/$file ]; then
+                vt_linux_parse_initrd_grub  file  (loop)/$file
+            fi
+        done
+        
+        vt_linux_initrd_count initrd_count
+        
+        # special process for special distros
+        if vt_cmp $initrd_count eq 0; then   
+            if [ -d (loop)/loader/entries ]; then
+                set LoadIsoEfiDriver=on
+                vt_linux_parse_initrd_grub  dir  (loop)/loader/entries/
+            elif [ -d (loop)/boot/grub ]; then
+                vt_linux_parse_initrd_grub  dir  (loop)/boot/grub/
+            fi
+        fi
+        
+        vt_linux_initrd_count initrd_count
+        if vt_cmp $initrd_count eq 0; then   
+            distro_specify_initrd_file
+        fi
+        
+        locate_initrd
+    fi
+    
+    vt_linux_chain_data ${1}${chosen_path}
+
+    if [ -n "$vtoy_chain_mem_addr" ]; then
+        terminal_output  console       
+        chainloader ${vtoy_path}/ventoy_x64.efi  env_param=${env_param} isoefi=${LoadIsoEfiDriver} ${vtdebug_flag} mem:${vtoy_chain_mem_addr}:size:${vtoy_chain_mem_size}
+        boot
+    else
+        echo "chain empty failed"
+        sleep 5
+    fi
+}
+
+
+function uefi_iso_menu_func {
+
+    if [ -d (loop)/ ]; then
+        loopback -d loop
+    fi
+
+    unset LoadIsoEfiDriver
+   
+    vt_chosen_img_path chosen_path
+
+    if vt_is_udf ${1}${chosen_path}; then
+        set ventoy_fs_probe=udf
+    else
+        set ventoy_fs_probe=iso9660
+    fi
+
+    loopback loop ${1}${chosen_path}
+    get_os_type (loop)
+    
+    vt_check_compatible (loop)
+    
+    vt_img_sector ${1}${chosen_path}
+    
+    if [ "$vtoy_os" = "Windows" ]; then
+        uefi_windows_menu_func  $1
+    else
+        uefi_linux_menu_func  $1
+    fi
+
+    terminal_output  gfxterm    
+}
+
+function legacy_windows_menu_func {
+    vt_windows_reset
+    
+    if [ "$ventoy_compatible" = "NO" ]; then                
+        find_wim_file (loop)
+        if [ -n "$ventoy_wim_file" ]; then
+            vt_windows_locate_wim $ventoy_wim_file
+        elif [ -n "${vtdebug_flag}" ]; then
+            echo No wim file found
+        fi
+    fi
+       
+    vt_windows_chain_data ${1}${chosen_path}
+    
+    if [ -n "${vtdebug_flag}" ]; then
+        sleep 5
+    fi
+    
+    if [ -n "$vtoy_chain_mem_addr" ]; then
+        linux16   $vtoy_path/ipxe.krn ${vtdebug_flag} ibft
+        initrd16  mem:${vtoy_chain_mem_addr}:size:${vtoy_chain_mem_size}        
+        boot
+    else
+        echo "chain empty failed"
+        sleep 5
+    fi
+}
+
+function legacy_linux_menu_func {
+    if [ "$ventoy_compatible" = "NO" ]; then
+    
+        vt_load_cpio  $vtoy_path/ventoy.cpio
+
+        vt_linux_clear_initrd
+                
+        for dir in "isolinux" "boot/isolinux" "boot/x86_64/loader" "syslinux" "boot/syslinux"; do
+            if [ -d (loop)/$dir ]; then
+                vt_linux_parse_initrd_isolinux   (loop)/$dir/
+            fi
+        done
+        
+        vt_linux_initrd_count initrd_count
+        
+        # special process for special distros
+        if vt_cmp $initrd_count eq 0; then   
+            #archlinux
+            if [ -d (loop)/arch/boot/syslinux ]; then
+                vt_linux_parse_initrd_isolinux   (loop)/arch/boot/syslinux/  /arch/
+                vt_linux_parse_initrd_isolinux   (loop)/arch/boot/syslinux/  /arch/boot/syslinux/
+                
+            #manjaro
+            elif [ -d (loop)/manjaro ]; then
+                if [ -e (loop)/boot/grub/kernels.cfg ]; then
+                    vt_linux_parse_initrd_grub  file  (loop)/boot/grub/kernels.cfg
+                fi
+            elif [ -e (loop)/boot/grub/grub.cfg ]; then                
+                vt_linux_parse_initrd_grub  file  (loop)/boot/grub/grub.cfg
+            fi
+        fi
+        
+        vt_linux_initrd_count initrd_count
+        if vt_cmp $initrd_count eq 0; then   
+            distro_specify_initrd_file
+        fi
+        
+        locate_initrd
+    fi
+    
+    vt_linux_chain_data ${1}${chosen_path}
+    
+    if [ -n "${vtdebug_flag}" ]; then
+        sleep 5
+    fi
+    
+    if [ -n "$vtoy_chain_mem_addr" ]; then        
+        linux16   $vtoy_path/ipxe.krn ${vtdebug_flag}
+        initrd16  mem:${vtoy_chain_mem_addr}:size:${vtoy_chain_mem_size}        
+        boot
+    else
+        echo "chain empty failed"
+        sleep 5
+    fi
+}
+
+function legacy_iso_menu_func {
+
+    if [ -d (loop)/ ]; then
+        loopback -d loop
+    fi
+
+    vt_chosen_img_path chosen_path
+
+    if vt_is_udf ${1}${chosen_path}; then
+        set ventoy_fs_probe=udf
+    else
+        set ventoy_fs_probe=iso9660
+    fi   
+
+    loopback loop ${1}${chosen_path}
+    
+    get_os_type (loop)
+    
+    vt_check_compatible (loop)
+    
+    vt_img_sector ${1}${chosen_path}
+   
+    if [ "$vtoy_os" = "Windows" ]; then
+        legacy_windows_menu_func  $1
+    else
+        legacy_linux_menu_func  $1
+    fi
+}
+
+
+
+
+
+#############################################################
+#############################################################
+#############################################################
+#######                 Main Process              ###########
+#############################################################
+#############################################################
+#############################################################
+
+set VENTOY_VERSION="1.0.00"
+
+#disable timeout
+unset timeout
+
+vt_device $root  vtoy_dev
+
+if [ "$vtoy_dev" = "tftp" ]; then
+    set vtoy_path=($root)    
+    for vtid in 0 1 2 3; do
+        if [ -d (hd$vtid,2)/grub ]; then
+            set iso_path=(hd$vtid,1)
+            break
+        fi
+    done
+else
+    set vtoy_path=($root)/ventoy    
+    set iso_path=($vtoy_dev,1)
+fi
+
+loadfont ascii
+
+if [ -f $iso_path/ventoy/ventoy.json ]; then
+   vt_load_plugin $iso_path
+fi
+
+terminal_output  gfxterm
+
+if [ -n "$vtoy_theme" ]; then
+    set theme=$vtoy_theme
+else
+    set theme=$prefix/themes/ventoy/theme.txt
+fi
+
+if [ -n "$vtoy_gfxmode" ]; then
+    set gfxmode=$vtoy_gfxmode
+else
+    set gfxmode=1024x768
+fi
+
+#colect all image files (iso files)
+set ventoy_img_count=0
+vt_list_img $iso_path ventoy_img_count
+
+#Dynamic menu for every iso file
+if vt_cmp $ventoy_img_count ne 0; then
+    set imgid=0
+    while vt_cmp $imgid lt $ventoy_img_count; do       
+        vt_img_name $imgid img_name
+        menuentry "$img_name" {  
+            if [ "$grub_platform" = "pc" ]; then
+                legacy_iso_menu_func $iso_path
+            else
+                uefi_iso_menu_func $iso_path                
+            fi            
+        }
+        
+        vt_incr imgid 1
+    done
+else
+    menuentry "No ISO files found (Press enter to reboot ...)" {
+        echo -e "\n    Rebooting ... "
+        reboot
+    }
+fi
+
diff --git a/INSTALL/grub/i386-pc/boot.img b/INSTALL/grub/i386-pc/boot.img
new file mode 100644 (file)
index 0000000..4b6f21c
Binary files /dev/null and b/INSTALL/grub/i386-pc/boot.img differ
diff --git a/INSTALL/grub/i386-pc/core.img b/INSTALL/grub/i386-pc/core.img
new file mode 100644 (file)
index 0000000..c97d4a1
Binary files /dev/null and b/INSTALL/grub/i386-pc/core.img differ
diff --git a/INSTALL/grub/themes/ventoy/background.png b/INSTALL/grub/themes/ventoy/background.png
new file mode 100644 (file)
index 0000000..5464b1d
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/background.png differ
diff --git a/INSTALL/grub/themes/ventoy/menu_c.png b/INSTALL/grub/themes/ventoy/menu_c.png
new file mode 100644 (file)
index 0000000..75c165b
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/menu_c.png differ
diff --git a/INSTALL/grub/themes/ventoy/menu_e.png b/INSTALL/grub/themes/ventoy/menu_e.png
new file mode 100644 (file)
index 0000000..d4c7421
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/menu_e.png differ
diff --git a/INSTALL/grub/themes/ventoy/menu_n.png b/INSTALL/grub/themes/ventoy/menu_n.png
new file mode 100644 (file)
index 0000000..5af3469
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/menu_n.png differ
diff --git a/INSTALL/grub/themes/ventoy/menu_ne.png b/INSTALL/grub/themes/ventoy/menu_ne.png
new file mode 100644 (file)
index 0000000..8757868
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/menu_ne.png differ
diff --git a/INSTALL/grub/themes/ventoy/menu_nw.png b/INSTALL/grub/themes/ventoy/menu_nw.png
new file mode 100644 (file)
index 0000000..8757868
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/menu_nw.png differ
diff --git a/INSTALL/grub/themes/ventoy/menu_s.png b/INSTALL/grub/themes/ventoy/menu_s.png
new file mode 100644 (file)
index 0000000..6ba2734
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/menu_s.png differ
diff --git a/INSTALL/grub/themes/ventoy/menu_se.png b/INSTALL/grub/themes/ventoy/menu_se.png
new file mode 100644 (file)
index 0000000..959b609
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/menu_se.png differ
diff --git a/INSTALL/grub/themes/ventoy/menu_sw.png b/INSTALL/grub/themes/ventoy/menu_sw.png
new file mode 100644 (file)
index 0000000..959b609
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/menu_sw.png differ
diff --git a/INSTALL/grub/themes/ventoy/menu_w.png b/INSTALL/grub/themes/ventoy/menu_w.png
new file mode 100644 (file)
index 0000000..d4c7421
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/menu_w.png differ
diff --git a/INSTALL/grub/themes/ventoy/select_c.png b/INSTALL/grub/themes/ventoy/select_c.png
new file mode 100644 (file)
index 0000000..245259a
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/select_c.png differ
diff --git a/INSTALL/grub/themes/ventoy/slider_c.png b/INSTALL/grub/themes/ventoy/slider_c.png
new file mode 100644 (file)
index 0000000..7d630fd
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/slider_c.png differ
diff --git a/INSTALL/grub/themes/ventoy/slider_n.png b/INSTALL/grub/themes/ventoy/slider_n.png
new file mode 100644 (file)
index 0000000..41482c9
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/slider_n.png differ
diff --git a/INSTALL/grub/themes/ventoy/slider_s.png b/INSTALL/grub/themes/ventoy/slider_s.png
new file mode 100644 (file)
index 0000000..17adc2a
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/slider_s.png differ
diff --git a/INSTALL/grub/themes/ventoy/terminal_box_c.png b/INSTALL/grub/themes/ventoy/terminal_box_c.png
new file mode 100644 (file)
index 0000000..d0dd52a
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/terminal_box_c.png differ
diff --git a/INSTALL/grub/themes/ventoy/terminal_box_e.png b/INSTALL/grub/themes/ventoy/terminal_box_e.png
new file mode 100644 (file)
index 0000000..394cbe4
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/terminal_box_e.png differ
diff --git a/INSTALL/grub/themes/ventoy/terminal_box_n.png b/INSTALL/grub/themes/ventoy/terminal_box_n.png
new file mode 100644 (file)
index 0000000..476f8bc
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/terminal_box_n.png differ
diff --git a/INSTALL/grub/themes/ventoy/terminal_box_ne.png b/INSTALL/grub/themes/ventoy/terminal_box_ne.png
new file mode 100644 (file)
index 0000000..9e26959
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/terminal_box_ne.png differ
diff --git a/INSTALL/grub/themes/ventoy/terminal_box_nw.png b/INSTALL/grub/themes/ventoy/terminal_box_nw.png
new file mode 100644 (file)
index 0000000..5c3cba8
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/terminal_box_nw.png differ
diff --git a/INSTALL/grub/themes/ventoy/terminal_box_s.png b/INSTALL/grub/themes/ventoy/terminal_box_s.png
new file mode 100644 (file)
index 0000000..85a8901
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/terminal_box_s.png differ
diff --git a/INSTALL/grub/themes/ventoy/terminal_box_se.png b/INSTALL/grub/themes/ventoy/terminal_box_se.png
new file mode 100644 (file)
index 0000000..d8627ee
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/terminal_box_se.png differ
diff --git a/INSTALL/grub/themes/ventoy/terminal_box_sw.png b/INSTALL/grub/themes/ventoy/terminal_box_sw.png
new file mode 100644 (file)
index 0000000..67c600c
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/terminal_box_sw.png differ
diff --git a/INSTALL/grub/themes/ventoy/terminal_box_w.png b/INSTALL/grub/themes/ventoy/terminal_box_w.png
new file mode 100644 (file)
index 0000000..d066e2d
Binary files /dev/null and b/INSTALL/grub/themes/ventoy/terminal_box_w.png differ
diff --git a/INSTALL/grub/themes/ventoy/theme.txt b/INSTALL/grub/themes/ventoy/theme.txt
new file mode 100644 (file)
index 0000000..bf794ec
--- /dev/null
@@ -0,0 +1,50 @@
+
+desktop-image: "background.png"
+title-text: " "
+title-font: "ascii"
+title-color: "#ffffff"
+message-font: "ascii"
+message-color: "#f2f2f2"
+
+terminal-box: "terminal_box_*.png"
+
++ boot_menu {
+  left = 10%
+  width = 80%
+  top = 30%
+  height = 50%
+
+  menu_pixmap_style = "menu_*.png"
+
+  item_font = "ascii"  
+  item_color = "#ffffff"  
+  item_height = 30
+  item_icon_space = 1
+  item_spacing = 1
+  item_padding = 1
+
+  selected_item_font = "ascii"
+  selected_item_color= "#f2f2f2"
+  selected_item_pixmap_style = "select_*.png"
+
+  #icon_height = 30
+  #icon_width = 30
+
+  scrollbar = true
+  scrollbar_width = 10
+  scrollbar_thumb = "slider_*.png"
+}
+
++ progress_bar {
+  id = "__timeout__"
+  text = "@TIMEOUT_NOTIFICATION_SHORT@"
+
+  left = 95%
+  width = 48
+  top = 95%
+  height = 48
+
+  text_color = "#f2f2f2"
+  bar_style = "*"
+  highlight_style = "*"
+}
diff --git a/INSTALL/grub/x86_64-efi/normal.mod b/INSTALL/grub/x86_64-efi/normal.mod
new file mode 100644 (file)
index 0000000..58d29ee
Binary files /dev/null and b/INSTALL/grub/x86_64-efi/normal.mod differ
diff --git a/INSTALL/tool/hexdump b/INSTALL/tool/hexdump
new file mode 100644 (file)
index 0000000..0fd90c2
Binary files /dev/null and b/INSTALL/tool/hexdump differ
diff --git a/INSTALL/tool/mkexfatfs_32 b/INSTALL/tool/mkexfatfs_32
new file mode 100644 (file)
index 0000000..a067639
Binary files /dev/null and b/INSTALL/tool/mkexfatfs_32 differ
diff --git a/INSTALL/tool/mkexfatfs_64 b/INSTALL/tool/mkexfatfs_64
new file mode 100644 (file)
index 0000000..5d8720c
Binary files /dev/null and b/INSTALL/tool/mkexfatfs_64 differ
diff --git a/INSTALL/tool/mount.exfat-fuse_32 b/INSTALL/tool/mount.exfat-fuse_32
new file mode 100644 (file)
index 0000000..0b3a5f1
Binary files /dev/null and b/INSTALL/tool/mount.exfat-fuse_32 differ
diff --git a/INSTALL/tool/mount.exfat-fuse_64 b/INSTALL/tool/mount.exfat-fuse_64
new file mode 100644 (file)
index 0000000..8f94524
Binary files /dev/null and b/INSTALL/tool/mount.exfat-fuse_64 differ
diff --git a/INSTALL/tool/ventoy_lib.sh b/INSTALL/tool/ventoy_lib.sh
new file mode 100644 (file)
index 0000000..3562927
--- /dev/null
@@ -0,0 +1,289 @@
+#!/bin/sh
+
+#Ventoy partition 32MB
+VENTOY_PART_SIZE=33554432
+VENTOY_SECTOR_SIZE=512
+VENTOY_SECTOR_NUM=65536
+
+ventoy_false() {
+    [ "1" = "2" ]
+}
+
+ventoy_true() {
+    [ "1" = "1" ]
+}
+
+ventoy_is_linux64() {
+    if [ -e /lib64 ]; then
+        ventoy_true
+        return
+    fi
+    
+    if [ -e /usr/lib64 ]; then
+        ventoy_true
+        return
+    fi
+    
+    if uname -a | egrep -q 'x86_64|amd64'; then
+        ventoy_true
+        return
+    fi
+    
+    ventoy_false
+}
+
+ventoy_is_dash() {
+    if [ -L /bin/sh ]; then
+        vtdst=$(readlink /bin/sh)
+        if [ "$vtdst" = "dash" ]; then
+            ventoy_true
+            return
+        fi
+    fi 
+    ventoy_false
+}
+
+vtinfo() {
+    if ventoy_is_dash; then
+        echo "\033[32m$*\033[0m"
+    else
+        echo -e "\033[32m$*\033[0m"
+    fi
+}
+
+vtwarn() {
+    if ventoy_is_dash; then
+        echo "\033[33m$*\033[0m"
+    else
+        echo -e "\033[33m$*\033[0m"
+    fi
+}
+
+
+vterr() {
+    if ventoy_is_dash; then
+        echo "\033[31m$*\033[0m"
+    else
+        echo -e "\033[31m$*\033[0m"
+    fi
+}
+
+vtdebug() {
+    echo "$*" >> ./log.txt
+}
+
+check_tool_work_ok() {
+    
+    if ventoy_is_linux64; then
+        vtdebug "This is linux 64"
+        mkexfatfs=mkexfatfs_64
+        vtoyfat=vtoyfat_64
+    else
+        vtdebug "This is linux 32"
+        mkexfatfs=mkexfatfs_32
+        vtoyfat=vtoyfat_32
+    fi
+    
+    if echo 1 | ./tool/hexdump > /dev/null; then
+        vtdebug "hexdump test ok ..."
+    else
+        vtdebug "hexdump test fail ..."
+        ventoy_false
+        return
+    fi
+   
+    if ./tool/$mkexfatfs -V > /dev/null; then
+        vtdebug "$mkexfatfs test ok ..."
+    else
+        vtdebug "$mkexfatfs test fail ..."
+        ventoy_false
+        return
+    fi
+    
+    if ./tool/$vtoyfat -T; then
+        vtdebug "$vtoyfat test ok ..."
+    else
+        vtdebug "$vtoyfat test fail ..."
+        ventoy_false
+        return
+    fi
+    
+    vtdebug "tool check success ..."
+    ventoy_true
+}
+
+
+get_disk_part_name() {
+    DISK=$1
+    
+    if echo $DISK | grep -q "/dev/loop"; then
+        echo ${DISK}p${2}
+    elif echo $DISK | grep -q "/dev/nvme[0-9][0-9]*n[0-9]"; then
+        echo ${DISK}p${2}
+    else
+        echo ${DISK}${2}
+    fi
+}
+
+
+get_ventoy_version_from_cfg() {
+    if grep -q 'set.*VENTOY_VERSION=' $1; then
+        grep 'set.*VENTOY_VERSION=' $1 | awk -F'"' '{print $2}'
+    else
+        echo 'none'
+    fi
+}
+
+is_disk_contains_ventoy() {
+    DISK=$1
+    
+    PART1=$(get_disk_part_name $1 1)  
+    PART2=$(get_disk_part_name $1 2)  
+    
+    if [ -e /sys/class/block/${PART2#/dev/}/size ]; then    
+        SIZE=$(cat /sys/class/block/${PART2#/dev/}/size)
+    else
+        SIZE=0
+    fi
+
+    if ! [ -b $PART1 ]; then
+        vtdebug "$PART1 not exist"
+        ventoy_false
+        return
+    fi
+    
+    if ! [ -b $PART2 ]; then
+        vtdebug "$PART2 not exist"
+        ventoy_false
+        return
+    fi
+    
+    PART2_TYPE=$(dd if=$DISK bs=1 count=1 skip=466 status=none | ./tool/hexdump -n1 -e  '1/1 "%02X"')
+    if [ "$PART2_TYPE" != "EF" ]; then
+        vtdebug "part2 type is $PART2_TYPE not EF"
+        ventoy_false
+        return
+    fi
+    
+    PART1_TYPE=$(dd if=$DISK bs=1 count=1 skip=450 status=none | ./tool/hexdump -n1 -e  '1/1 "%02X"')
+    if [ "$PART1_TYPE" != "07" ]; then
+        vtdebug "part1 type is $PART2_TYPE not 07"
+        ventoy_false
+        return
+    fi
+    
+    if [ -e /sys/class/block/${PART1#/dev/}/start ]; then
+        PART1_START=$(cat /sys/class/block/${PART1#/dev/}/start)
+    fi
+    
+    if [ "$PART1_START" != "2048" ]; then
+        vtdebug "part1 start is $PART1_START not 2048"
+        ventoy_false
+        return
+    fi 
+    
+    if [ "$VENTOY_SECTOR_NUM" != "$SIZE" ]; then
+        vtdebug "part2 size is $SIZE not $VENTOY_SECTOR_NUM"
+        ventoy_false
+        return
+    fi
+    
+    ventoy_true
+}
+
+get_disk_ventoy_version() {
+
+    if ! is_disk_contains_ventoy $1; then
+        ventoy_false
+        return
+    fi
+    
+    PART2=$(get_disk_part_name $1 2)    
+    
+    if ventoy_is_linux64; then
+        cmd=./tool/vtoyfat_64
+    else
+        cmd=./tool/vtoyfat_32
+    fi
+    
+    ParseVer=$($cmd $PART2)
+    if [ $? -eq 0 ]; then
+        vtdebug "Ventoy version in $PART2 is $ParseVer"
+        echo $ParseVer
+        ventoy_true
+        return
+    fi
+    
+    ventoy_false
+}
+
+
+format_ventoy_disk() {
+    DISK=$1
+    PART2=$(get_disk_part_name $DISK 2)
+    
+    sector_num=$(cat /sys/block/${DISK#/dev/}/size)
+    
+    part1_start_sector=2048
+    part1_end_sector=$(expr $sector_num - $VENTOY_SECTOR_NUM - 1)
+    export part2_start_sector=$(expr $part1_end_sector + 1)
+    part2_end_sector=$(expr $sector_num - 1)
+
+    if [ -e $PART2 ]; then
+        echo "delete $PART2"
+        rm -f $PART2
+    fi
+
+    echo ""
+    echo "Create partitions on $DISK ..."
+    
+fdisk $DISK >/dev/null 2>&1 <<EOF
+o
+n
+p
+1
+$part1_start_sector
+$part1_end_sector
+n
+p
+2
+$part2_start_sector
+$part2_end_sector
+t
+1
+7
+t
+2
+ef
+a
+2
+w
+EOF
+    
+    echo "Done"
+    udevadm trigger >/dev/null 2>&1
+    partprobe >/dev/null 2>&1
+    sleep 3
+
+
+    echo 'mkfs on disk partitions ...'
+    while ! [ -e $PART2 ]; do
+        echo "wait $PART2 ..."
+        sleep 1
+    done
+
+    echo "create efi fat fs ..."
+    for i in 0 1 2 3 4 5 6 7 8 9; do
+        if mkfs.vfat -F 16 -n EFI $PART2; then
+            echo 'success'
+            break
+        else
+            echo "$? retry ..."
+            sleep 2
+        fi
+    done
+}
+
+
+
+
diff --git a/INSTALL/tool/vtoy_gen_uuid b/INSTALL/tool/vtoy_gen_uuid
new file mode 100644 (file)
index 0000000..8fc29eb
Binary files /dev/null and b/INSTALL/tool/vtoy_gen_uuid differ
diff --git a/INSTALL/tool/vtoyfat_32 b/INSTALL/tool/vtoyfat_32
new file mode 100644 (file)
index 0000000..9ff7966
Binary files /dev/null and b/INSTALL/tool/vtoyfat_32 differ
diff --git a/INSTALL/tool/vtoyfat_64 b/INSTALL/tool/vtoyfat_64
new file mode 100644 (file)
index 0000000..83c9e02
Binary files /dev/null and b/INSTALL/tool/vtoyfat_64 differ
diff --git a/INSTALL/tool/xzcat b/INSTALL/tool/xzcat
new file mode 100644 (file)
index 0000000..012a984
Binary files /dev/null and b/INSTALL/tool/xzcat differ
diff --git a/INSTALL/ventoy/imdisk/32/imdisk.cpl b/INSTALL/ventoy/imdisk/32/imdisk.cpl
new file mode 100644 (file)
index 0000000..e59193c
Binary files /dev/null and b/INSTALL/ventoy/imdisk/32/imdisk.cpl differ
diff --git a/INSTALL/ventoy/imdisk/32/imdisk.exe b/INSTALL/ventoy/imdisk/32/imdisk.exe
new file mode 100644 (file)
index 0000000..c082730
Binary files /dev/null and b/INSTALL/ventoy/imdisk/32/imdisk.exe differ
diff --git a/INSTALL/ventoy/imdisk/32/imdisk.sys b/INSTALL/ventoy/imdisk/32/imdisk.sys
new file mode 100644 (file)
index 0000000..17b2ac2
Binary files /dev/null and b/INSTALL/ventoy/imdisk/32/imdisk.sys differ
diff --git a/INSTALL/ventoy/imdisk/64/imdisk.cpl b/INSTALL/ventoy/imdisk/64/imdisk.cpl
new file mode 100644 (file)
index 0000000..13f94b2
Binary files /dev/null and b/INSTALL/ventoy/imdisk/64/imdisk.cpl differ
diff --git a/INSTALL/ventoy/imdisk/64/imdisk.exe b/INSTALL/ventoy/imdisk/64/imdisk.exe
new file mode 100644 (file)
index 0000000..19cb383
Binary files /dev/null and b/INSTALL/ventoy/imdisk/64/imdisk.exe differ
diff --git a/INSTALL/ventoy/imdisk/64/imdisk.sys b/INSTALL/ventoy/imdisk/64/imdisk.sys
new file mode 100644 (file)
index 0000000..430229f
Binary files /dev/null and b/INSTALL/ventoy/imdisk/64/imdisk.sys differ
diff --git a/INSTALL/ventoy/ipxe.krn b/INSTALL/ventoy/ipxe.krn
new file mode 100644 (file)
index 0000000..61a8637
Binary files /dev/null and b/INSTALL/ventoy/ipxe.krn differ
diff --git a/INSTALL/ventoy/iso9660_x64.efi b/INSTALL/ventoy/iso9660_x64.efi
new file mode 100644 (file)
index 0000000..240c33f
Binary files /dev/null and b/INSTALL/ventoy/iso9660_x64.efi differ
diff --git a/INSTALL/ventoy/ventoy.cpio b/INSTALL/ventoy/ventoy.cpio
new file mode 100644 (file)
index 0000000..48b4faf
Binary files /dev/null and b/INSTALL/ventoy/ventoy.cpio differ
diff --git a/INSTALL/ventoy/ventoy_x64.efi b/INSTALL/ventoy/ventoy_x64.efi
new file mode 100644 (file)
index 0000000..342fca4
Binary files /dev/null and b/INSTALL/ventoy/ventoy_x64.efi differ
diff --git a/INSTALL/ventoy/vtoyjump32.exe b/INSTALL/ventoy/vtoyjump32.exe
new file mode 100644 (file)
index 0000000..24cd8ab
Binary files /dev/null and b/INSTALL/ventoy/vtoyjump32.exe differ
diff --git a/INSTALL/ventoy/vtoyjump64.exe b/INSTALL/ventoy/vtoyjump64.exe
new file mode 100644 (file)
index 0000000..f6bb947
Binary files /dev/null and b/INSTALL/ventoy/vtoyjump64.exe differ
diff --git a/IPXE/README.txt b/IPXE/README.txt
new file mode 100644 (file)
index 0000000..0eabadd
--- /dev/null
@@ -0,0 +1,8 @@
+
+========== About Source Code =============
+1. unpack ipxe_org_code/ipxe-3fe683e.tar.bz2
+2. After decompressing, delete ipxe-3fe683e/src/drivers (whole directory)
+3. Merge left source code with the ipxe-3fe683e directory here
+
+========== Build =============
+make bin/ipxe.iso 
diff --git a/IPXE/ipxe-3fe683e/src/arch/x86/core/runtime.c b/IPXE/ipxe-3fe683e/src/arch/x86/core/runtime.c
new file mode 100644 (file)
index 0000000..ed15de5
--- /dev/null
@@ -0,0 +1,276 @@
+/*
+ * Copyright (C) 2011 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * Command line and initrd passed to iPXE at runtime
+ *
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <errno.h>
+#include <assert.h>
+#include <ipxe/init.h>
+#include <ipxe/image.h>
+#include <ipxe/script.h>
+#include <ipxe/umalloc.h>
+#include <realmode.h>
+#include <ventoy.h>
+
+/** Command line physical address
+ *
+ * This can be set by the prefix.
+ */
+uint32_t __bss16 ( cmdline_phys );
+#define cmdline_phys __use_data16 ( cmdline_phys )
+
+/** initrd physical address
+ *
+ * This can be set by the prefix.
+ */
+uint32_t __bss16 ( initrd_phys );
+#define initrd_phys __use_data16 ( initrd_phys )
+
+/** initrd length
+ *
+ * This can be set by the prefix.
+ */
+uint32_t __bss16 ( initrd_len );
+#define initrd_len __use_data16 ( initrd_len )
+
+/** Internal copy of the command line */
+static char *cmdline_copy;
+
+/** Free command line image */
+static void cmdline_image_free ( struct refcnt *refcnt ) {
+       struct image *image = container_of ( refcnt, struct image, refcnt );
+
+       DBGC ( image, "RUNTIME freeing command line\n" );
+       free ( cmdline_copy );
+}
+
+/** Embedded script representing the command line */
+static struct image cmdline_image = {
+       .refcnt = REF_INIT ( cmdline_image_free ),
+       .name = "<CMDLINE>",
+       .type = &script_image_type,
+};
+
+/** Colour for debug messages */
+#define colour &cmdline_image
+
+/**
+ * Strip unwanted cruft from command line
+ *
+ * @v cmdline          Command line
+ * @v cruft            Initial substring of cruft to strip
+ */
+static void cmdline_strip ( char *cmdline, const char *cruft ) {
+       char *strip;
+       char *strip_end;
+
+       /* Find unwanted cruft, if present */
+       if ( ! ( strip = strstr ( cmdline, cruft ) ) )
+               return;
+
+       /* Strip unwanted cruft */
+       strip_end = strchr ( strip, ' ' );
+       if ( strip_end ) {
+               *strip_end = '\0';
+               DBGC ( colour, "RUNTIME stripping \"%s\"\n", strip );
+               strcpy ( strip, ( strip_end + 1 ) );
+       } else {
+               DBGC ( colour, "RUNTIME stripping \"%s\"\n", strip );
+               *strip = '\0';
+       }
+}
+
+/**
+ * Initialise command line
+ *
+ * @ret rc             Return status code
+ */
+static int cmdline_init ( void ) {
+       userptr_t cmdline_user;
+       char *cmdline;
+       size_t len;
+       int rc;
+
+       /* Do nothing if no command line was specified */
+       if ( ! cmdline_phys ) {
+               DBGC ( colour, "RUNTIME found no command line\n" );
+               return 0;
+       }
+       cmdline_user = phys_to_user ( cmdline_phys );
+       len = ( strlen_user ( cmdline_user, 0 ) + 1 /* NUL */ );
+
+       /* Allocate and copy command line */
+       cmdline_copy = malloc ( len );
+       if ( ! cmdline_copy ) {
+               DBGC ( colour, "RUNTIME could not allocate %zd bytes for "
+                      "command line\n", len );
+               rc = -ENOMEM;
+               goto err_alloc_cmdline_copy;
+       }
+       cmdline = cmdline_copy;
+       copy_from_user ( cmdline, cmdline_user, 0, len );
+       DBGC ( colour, "RUNTIME found command line \"%s\" at %08x\n",
+              cmdline, cmdline_phys );
+
+       /* Mark command line as consumed */
+       cmdline_phys = 0;
+
+       /* Strip unwanted cruft from the command line */
+       cmdline_strip ( cmdline, "BOOT_IMAGE=" );
+       cmdline_strip ( cmdline, "initrd=" );
+       while ( isspace ( *cmdline ) )
+               cmdline++;
+       DBGC ( colour, "RUNTIME using command line \"%s\"\n", cmdline );
+
+       /* Prepare and register image */
+       cmdline_image.data = virt_to_user ( cmdline );
+       cmdline_image.len = strlen ( cmdline );
+       if ( cmdline_image.len ) {
+               if ( ( rc = register_image ( &cmdline_image ) ) != 0 ) {
+                       DBGC ( colour, "RUNTIME could not register command "
+                              "line: %s\n", strerror ( rc ) );
+                       goto err_register_image;
+               }
+       }
+
+       /* Drop our reference to the image */
+       image_put ( &cmdline_image );
+
+       return 0;
+
+ err_register_image:
+       image_put ( &cmdline_image );
+ err_alloc_cmdline_copy:
+       return rc;
+}
+
+/**
+ * Initialise initrd
+ *
+ * @ret rc             Return status code
+ */
+static int initrd_init ( void ) {
+       struct image *image;
+       int rc;
+
+       /* Do nothing if no initrd was specified */
+       if ( ! initrd_phys ) {
+               DBGC ( colour, "RUNTIME found no initrd\n" );
+               return 0;
+       }
+       if ( ! initrd_len ) {
+               DBGC ( colour, "RUNTIME found empty initrd\n" );
+               return 0;
+       }
+       DBGC ( colour, "RUNTIME found initrd at [%x,%x)\n",
+              initrd_phys, ( initrd_phys + initrd_len ) );
+
+       /* Allocate image */
+       image = alloc_image ( NULL );
+       if ( ! image ) {
+               DBGC ( colour, "RUNTIME could not allocate image for "
+                      "initrd\n" );
+               rc = -ENOMEM;
+               goto err_alloc_image;
+       }
+       if ( ( rc = image_set_name ( image, "<INITRD>" ) ) != 0 ) {
+               DBGC ( colour, "RUNTIME could not set image name: %s\n",
+                      strerror ( rc ) );
+               goto err_set_name;
+       }
+
+       /* Allocate and copy initrd content */
+       image->data = umalloc ( initrd_len );
+       if ( ! image->data ) {
+               DBGC ( colour, "RUNTIME could not allocate %d bytes for "
+                      "initrd\n", initrd_len );
+               rc = -ENOMEM;
+               goto err_umalloc;
+       }
+       image->len = initrd_len;
+       memcpy_user ( image->data, 0, phys_to_user ( initrd_phys ), 0,
+                     initrd_len );
+
+    g_initrd_addr = (void *)image->data;  
+    g_initrd_len = image->len;  
+    g_cmdline_copy = cmdline_copy;
+
+
+       /* Mark initrd as consumed */
+       initrd_phys = 0;
+
+       /* Register image */
+       if ( ( rc = register_image ( image ) ) != 0 ) {
+               DBGC ( colour, "RUNTIME could not register initrd: %s\n",
+                      strerror ( rc ) );
+               goto err_register_image;
+       }
+
+       /* Drop our reference to the image */
+       image_put ( image );
+
+       return 0;
+
+ err_register_image:
+ err_umalloc:
+ err_set_name:
+       image_put ( image );
+ err_alloc_image:
+       return rc;
+}
+
+/**
+ * Initialise command line and initrd
+ *
+ */
+static void runtime_init ( void ) {
+       int rc;
+
+       /* Initialise command line */
+       if ( ( rc = cmdline_init() ) != 0 ) {
+               /* No way to report failure */
+               return;
+       }
+
+       /* Initialise initrd */
+       if ( ( rc = initrd_init() ) != 0 ) {
+               /* No way to report failure */
+               return;
+       }
+}
+
+/** Command line and initrd initialisation function */
+struct startup_fn runtime_startup_fn __startup_fn ( STARTUP_NORMAL ) = {
+       .name = "runtime",
+       .startup = runtime_init,
+};
diff --git a/IPXE/ipxe-3fe683e/src/arch/x86/core/ventoy_vdisk.c b/IPXE/ipxe-3fe683e/src/arch/x86/core/ventoy_vdisk.c
new file mode 100644 (file)
index 0000000..b283b08
--- /dev/null
@@ -0,0 +1,527 @@
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <limits.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <assert.h>
+#include <ipxe/blockdev.h>
+#include <ipxe/io.h>
+#include <ipxe/acpi.h>
+#include <ipxe/sanboot.h>
+#include <ipxe/device.h>
+#include <ipxe/pci.h>
+#include <ipxe/eltorito.h>
+#include <ipxe/timer.h>
+#include <ipxe/umalloc.h>
+#include <realmode.h>
+#include <bios.h>
+#include <biosint.h>
+#include <bootsector.h>
+#include <int13.h>
+#include <ventoy.h>
+
+int g_debug = 0;
+char *g_cmdline_copy;
+void *g_initrd_addr;
+size_t g_initrd_len;
+ventoy_chain_head *g_chain;
+ventoy_img_chunk *g_chunk;
+uint32_t g_img_chunk_num;
+ventoy_img_chunk *g_cur_chunk;
+uint32_t g_disk_sector_size;
+
+ventoy_override_chunk *g_override_chunk;
+uint32_t g_override_chunk_num;
+
+ventoy_virt_chunk *g_virt_chunk;
+uint32_t g_virt_chunk_num;
+
+ventoy_sector_flag g_sector_flag[128];
+
+static struct int13_disk_address __bss16 ( ventoy_address );
+#define ventoy_address __use_data16 ( ventoy_address )
+
+static uint64_t ventoy_remap_lba(uint64_t lba, uint32_t *count)
+{
+    uint32_t i;
+    uint32_t max_sectors;
+    ventoy_img_chunk *cur;
+
+    if ((NULL == g_cur_chunk) || ((lba) < g_cur_chunk->img_start_sector) || ((lba) > g_cur_chunk->img_end_sector))
+    {
+        g_cur_chunk = NULL;
+        for (i = 0; i < g_img_chunk_num; i++)
+        {
+            cur = g_chunk + i;
+            if (lba >= cur->img_start_sector && lba <= cur->img_end_sector)
+            {
+                g_cur_chunk = cur;
+                break;
+            }
+        }
+    }
+
+    if (g_cur_chunk)
+    {
+        max_sectors = g_cur_chunk->img_end_sector - lba + 1;
+        if (*count > max_sectors)
+        {
+            *count = max_sectors;
+        }
+
+        if (512 == g_disk_sector_size)
+        {
+            return g_cur_chunk->disk_start_sector + ((lba - g_cur_chunk->img_start_sector) << 2);            
+        }
+        return g_cur_chunk->disk_start_sector + (lba - g_cur_chunk->img_start_sector) * 2048 / g_disk_sector_size;
+    }
+    return lba;
+}
+
+static int ventoy_vdisk_read_real(uint64_t lba, unsigned int count, unsigned long buffer)
+{
+    uint32_t i = 0;
+    uint32_t left = 0;
+    uint32_t readcount = 0;
+    uint32_t tmpcount = 0;
+    uint16_t status = 0;
+    uint64_t curlba = 0;
+    uint64_t maplba = 0;
+    uint64_t start = 0;
+    uint64_t end = 0;
+    uint64_t override_start = 0;
+    uint64_t override_end = 0;
+    unsigned long phyaddr;
+    unsigned long databuffer = buffer;
+    uint8_t *override_data;
+
+    curlba = lba;
+    left = count;
+
+    while (left > 0)
+    {
+        readcount = left;
+        maplba = ventoy_remap_lba(curlba, &readcount);
+        
+        if (g_disk_sector_size == 512)
+        {
+            tmpcount = (readcount << 2);
+        }
+        else
+        {
+            tmpcount = (readcount * 2048) / g_disk_sector_size;
+        }
+
+        phyaddr = user_to_phys(buffer, 0);
+
+        while (tmpcount > 0)
+        {
+            /* Use INT 13, 42 to read the data from real disk */
+            ventoy_address.lba = maplba;
+            ventoy_address.buffer.segment = (uint16_t)(phyaddr >> 4);
+           ventoy_address.buffer.offset = (uint16_t)(phyaddr & 0x0F);
+
+            if (tmpcount >= 64) /* max sectors per transmit */
+            {
+                ventoy_address.count = 64;
+                tmpcount -= 64;
+                maplba   += 64;
+                phyaddr  += 32768;
+            }
+            else
+            {
+                ventoy_address.count = tmpcount;
+                tmpcount = 0;
+            }
+
+            __asm__ __volatile__ ( REAL_CODE ( "stc\n\t"
+                                          "sti\n\t"
+                                          "int $0x13\n\t"
+                                          "sti\n\t" /* BIOS bugs */
+                                          "jc 1f\n\t"
+                                          "xorw %%ax, %%ax\n\t"
+                                          "\n1:\n\t" )
+                              : "=a" ( status )
+                              : "a" ( 0x4200 ), "d" ( VENTOY_BIOS_FAKE_DRIVE ),
+                                "S" ( __from_data16 ( &ventoy_address ) ) );
+        }
+
+        curlba += readcount;
+        left -= readcount;
+        buffer += (readcount * 2048);
+    }
+
+    start = lba * 2048;
+    if (start > g_chain->real_img_size_in_bytes)
+    {
+        goto end;
+    }
+
+    end = start + count * 2048;
+    for (i = 0; i < g_override_chunk_num; i++)
+    {
+        override_data = g_override_chunk[i].override_data;
+        override_start = g_override_chunk[i].img_offset;
+        override_end = override_start + g_override_chunk[i].override_size;
+
+        if (end <= override_start || start >= override_end)
+        {
+            continue;
+        }
+
+        if (start <= override_start)
+        {
+            if (end <= override_end)
+            {
+                memcpy((char *)databuffer + override_start - start, override_data, end - override_start);  
+            }
+            else
+            {
+                memcpy((char *)databuffer + override_start - start, override_data, override_end - override_start);
+            }
+        }
+        else
+        {
+            if (end <= override_end)
+            {
+                memcpy((char *)databuffer, override_data + start - override_start, end - start);     
+            }
+            else
+            {
+                memcpy((char *)databuffer, override_data + start - override_start, override_end - start);
+            }
+        }
+    }
+
+end:
+
+    return 0;
+}
+
+int ventoy_vdisk_read(struct san_device *sandev, uint64_t lba, unsigned int count, unsigned long buffer)
+{
+    uint32_t i, j;
+    uint64_t curlba;
+    uint64_t lastlba = 0;
+    uint32_t lbacount = 0;
+    unsigned long lastbuffer;
+    uint64_t readend;
+    ventoy_virt_chunk *node;
+    ventoy_sector_flag *cur_flag;
+    ventoy_sector_flag *sector_flag = g_sector_flag;
+    struct i386_all_regs *ix86;
+
+    if (INT13_EXTENDED_READ != sandev->int13_command)
+    {
+        DBGC(sandev, "invalid cmd %u\n", sandev->int13_command);
+        return 0;
+    }
+
+    ix86 = (struct i386_all_regs *)sandev->x86_regptr;
+
+    readend = (lba + count) * 2048;
+    if (readend < g_chain->real_img_size_in_bytes)
+    {
+        ventoy_vdisk_read_real(lba, count, buffer);
+        ix86->regs.dl = sandev->drive;
+        return 0;
+    }
+
+    if (count > sizeof(g_sector_flag))
+    {
+        sector_flag = (ventoy_sector_flag *)malloc(count * sizeof(ventoy_sector_flag));
+    }
+
+    for (curlba = lba, cur_flag = sector_flag, j = 0; j < count; j++, curlba++, cur_flag++)
+    {
+        cur_flag->flag = 0;
+        for (node = g_virt_chunk, i = 0; i < g_virt_chunk_num; i++, node++)
+        {
+            if (curlba >= node->mem_sector_start && curlba < node->mem_sector_end)
+            {
+                memcpy((void *)(buffer + j * 2048), 
+                       (char *)g_virt_chunk + node->mem_sector_offset + (curlba - node->mem_sector_start) * 2048,
+                       2048);
+                cur_flag->flag = 1;
+                break;
+            }
+            else if (curlba >= node->remap_sector_start && curlba < node->remap_sector_end)
+            {
+                cur_flag->remap_lba = node->org_sector_start + curlba - node->remap_sector_start;
+                cur_flag->flag = 2;
+                break;
+            }
+        }
+    }
+
+    for (curlba = lba, cur_flag = sector_flag, j = 0; j < count; j++, curlba++, cur_flag++)
+    {
+        if (cur_flag->flag == 2)
+        {
+            if (lastlba == 0)
+            {
+                lastbuffer = buffer + j * 2048;
+                lastlba = cur_flag->remap_lba;
+                lbacount = 1;
+            }
+            else if (lastlba + lbacount == cur_flag->remap_lba)
+            {
+                lbacount++;
+            }
+            else
+            {
+                ventoy_vdisk_read_real(lastlba, lbacount, lastbuffer);
+                lastbuffer = buffer + j * 2048;
+                lastlba = cur_flag->remap_lba;
+                lbacount = 1;
+            }
+        }
+    }
+
+    if (lbacount > 0)
+    {
+        ventoy_vdisk_read_real(lastlba, lbacount, lastbuffer);
+    }
+
+    if (sector_flag != g_sector_flag)
+    {
+        free(sector_flag);
+    }
+
+    ix86->regs.dl = sandev->drive;
+    return 0;
+}
+
+static void ventoy_dump_img_chunk(ventoy_chain_head *chain)
+{
+    uint32_t i;
+    ventoy_img_chunk *chunk;
+
+    chunk = (ventoy_img_chunk *)((char *)chain + chain->img_chunk_offset);
+
+    printf("##################### ventoy_dump_img_chunk #######################\n");
+
+    for (i = 0; i < chain->img_chunk_num; i++)
+    {
+        printf("%2u: [ %u - %u ] <==> [ %llu - %llu ]\n",
+               i, chunk[i].img_start_sector, chunk[i].img_end_sector, 
+               chunk[i].disk_start_sector, chunk[i].disk_end_sector);
+    }
+    
+    ventoy_debug_pause();
+}
+
+static void ventoy_dump_override_chunk(ventoy_chain_head *chain)
+{
+    uint32_t i;
+    ventoy_override_chunk *chunk;
+    
+    chunk = (ventoy_override_chunk *)((char *)chain + chain->override_chunk_offset);
+
+    printf("##################### ventoy_dump_override_chunk #######################\n");
+
+    for (i = 0; i < g_override_chunk_num; i++)
+    {
+        printf("%2u: [ %llu, %u ]\n", i, chunk[i].img_offset, chunk[i].override_size);
+    }
+    
+    ventoy_debug_pause();
+}
+
+static void ventoy_dump_virt_chunk(ventoy_chain_head *chain)
+{
+    uint32_t i;
+    ventoy_virt_chunk *node;
+     
+    printf("##################### ventoy_dump_virt_chunk #######################\n");
+    printf("virt_chunk_offset=%u\n", chain->virt_chunk_offset);
+    printf("virt_chunk_num=%u\n",    chain->virt_chunk_num);
+
+    node = (ventoy_virt_chunk *)((char *)chain + chain->virt_chunk_offset);
+    for (i = 0; i < chain->virt_chunk_num; i++, node++)
+    {
+        printf("%2u: mem:[ %u, %u, %u ]  remap:[ %u, %u, %u ]\n", i, 
+               node->mem_sector_start,
+               node->mem_sector_end,
+               node->mem_sector_offset,
+               node->remap_sector_start,
+               node->remap_sector_end,
+               node->org_sector_start);
+    }
+    
+    ventoy_debug_pause();
+}
+
+static void ventoy_dump_chain(ventoy_chain_head *chain)
+{
+    uint32_t i = 0;
+    uint8_t chksum = 0;
+    uint8_t *guid;
+    
+    guid = chain->os_param.vtoy_disk_guid;
+    for (i = 0; i < sizeof(ventoy_os_param); i++)
+    {
+        chksum += *((uint8_t *)(&(chain->os_param)) + i);
+    }
+
+    printf("##################### ventoy_dump_chain #######################\n");
+
+    printf("os_param will be save at %p\n", ventoy_get_runtime_addr());
+
+    printf("os_param->chksum=0x%x (%s)\n", chain->os_param.chksum, chksum ? "FAILED" : "SUCCESS");
+    printf("os_param->vtoy_disk_guid=%02x%02x%02x%02x\n", guid[0], guid[1], guid[2], guid[3]);
+    printf("os_param->vtoy_disk_size=%llu\n",     chain->os_param.vtoy_disk_size);
+    printf("os_param->vtoy_disk_part_id=%u\n",    chain->os_param.vtoy_disk_part_id);
+    printf("os_param->vtoy_disk_part_type=%u\n",  chain->os_param.vtoy_disk_part_type);
+    printf("os_param->vtoy_img_path=<%s>\n",      chain->os_param.vtoy_img_path);
+    printf("os_param->vtoy_img_size=<%llu>\n",    chain->os_param.vtoy_img_size);
+    printf("os_param->vtoy_img_location_addr=<0x%llx>\n", chain->os_param.vtoy_img_location_addr);
+    printf("os_param->vtoy_img_location_len=<%u>\n",   chain->os_param.vtoy_img_location_len);
+    ventoy_debug_pause();
+
+    printf("chain->disk_drive=0x%x\n",          chain->disk_drive);
+    printf("chain->drive_map=0x%x\n",           chain->drive_map);
+    printf("chain->disk_sector_size=%u\n",      chain->disk_sector_size);
+    printf("chain->real_img_size_in_bytes=%llu\n",   chain->real_img_size_in_bytes);
+    printf("chain->virt_img_size_in_bytes=%llu\n", chain->virt_img_size_in_bytes);
+    printf("chain->boot_catalog=%u\n",          chain->boot_catalog);
+    printf("chain->img_chunk_offset=%u\n",      chain->img_chunk_offset);
+    printf("chain->img_chunk_num=%u\n",         chain->img_chunk_num);
+    printf("chain->override_chunk_offset=%u\n", chain->override_chunk_offset);
+    printf("chain->override_chunk_num=%u\n",    chain->override_chunk_num);
+    printf("chain->virt_chunk_offset=%u\n",    chain->virt_chunk_offset);
+    printf("chain->virt_chunk_num=%u\n",    chain->virt_chunk_num);
+    ventoy_debug_pause();
+
+    ventoy_dump_img_chunk(chain);
+    ventoy_dump_override_chunk(chain);
+    ventoy_dump_virt_chunk(chain);
+}
+
+static int ventoy_update_image_location(ventoy_os_param *param)
+{
+    uint8_t chksum = 0;
+    unsigned int i;
+    unsigned int length;
+    userptr_t address = 0;
+    ventoy_image_location *location = NULL;
+    ventoy_image_disk_region *region = NULL;
+    ventoy_img_chunk *chunk = g_chunk;
+
+    length = sizeof(ventoy_image_location) + (g_img_chunk_num - 1) * sizeof(ventoy_image_disk_region);
+
+    address = umalloc(length + 4096 * 2);
+    if (!address)
+    {
+        return 0;
+    }
+
+    if (address % 4096)
+    {
+        address += 4096 - (address % 4096);
+    }
+
+    param->chksum = 0;
+    param->vtoy_img_location_addr = user_to_phys(address, 0);
+    param->vtoy_img_location_len = length;
+
+    /* update check sum */
+    for (i = 0; i < sizeof(ventoy_os_param); i++)
+    {
+        chksum += *((uint8_t *)param + i);
+    }
+    param->chksum = (chksum == 0) ? 0 : (uint8_t)(0x100 - chksum);
+
+    location = (ventoy_image_location *)(unsigned long)(address);
+    if (NULL == location)
+    {
+        return 0;
+    }
+    
+    memcpy(&location->guid, &param->guid, sizeof(ventoy_guid));
+    location->image_sector_size = 2048;
+    location->disk_sector_size  = g_chain->disk_sector_size;
+    location->region_count = g_img_chunk_num;
+
+    region = location->regions;
+
+    for (i = 0; i < g_img_chunk_num; i++)
+    {
+        region->image_sector_count = chunk->img_end_sector - chunk->img_start_sector + 1;
+        region->image_start_sector = chunk->img_start_sector;
+        region->disk_start_sector  = chunk->disk_start_sector;
+        region++;
+        chunk++;
+    }
+
+    return 0;
+}
+
+int ventoy_boot_vdisk(void *data)
+{
+    uint8_t chksum = 0;
+    unsigned int i;
+    unsigned int drive;
+    
+    (void)data;
+
+    ventoy_address.bufsize = offsetof ( typeof ( ventoy_address ), buffer_phys );
+
+    if (strstr(g_cmdline_copy, "debug"))
+    {
+        g_debug = 1;
+        printf("### ventoy chain boot begin... ###\n");
+        ventoy_debug_pause();
+    }
+
+    g_chain = (ventoy_chain_head *)g_initrd_addr;
+    g_chunk = (ventoy_img_chunk *)((char *)g_chain + g_chain->img_chunk_offset);
+    g_img_chunk_num = g_chain->img_chunk_num;
+    g_disk_sector_size = g_chain->disk_sector_size;
+    g_cur_chunk = g_chunk;
+
+    g_override_chunk = (ventoy_override_chunk *)((char *)g_chain + g_chain->override_chunk_offset);
+    g_override_chunk_num = g_chain->override_chunk_num;
+
+    g_virt_chunk = (ventoy_virt_chunk *)((char *)g_chain + g_chain->virt_chunk_offset);
+    g_virt_chunk_num = g_chain->virt_chunk_num;
+
+    if (g_debug)
+    {
+        for (i = 0; i < sizeof(ventoy_os_param); i++)
+        {
+            chksum += *((uint8_t *)(&(g_chain->os_param)) + i);
+        }
+        printf("os param checksum: 0x%x %s\n", g_chain->os_param.chksum, chksum ? "FAILED" : "SUCCESS");
+    }
+
+    ventoy_update_image_location(&(g_chain->os_param));
+
+    if (g_debug)
+    {
+        ventoy_dump_chain(g_chain);
+    }
+
+    drive = ventoy_int13_hook(g_chain);
+
+    if (g_debug)
+    {
+        printf("### ventoy chain boot before boot image ... ###\n");
+        ventoy_debug_pause();    
+    }
+    
+    ventoy_int13_boot(drive, &(g_chain->os_param), g_cmdline_copy);
+
+    if (g_debug)
+    {
+        printf("!!!!!!!!!! ventoy boot failed !!!!!!!!!!\n");
+        ventoy_debug_pause();
+    }
+
+    return 0;
+}
+
diff --git a/IPXE/ipxe-3fe683e/src/arch/x86/drivers/hyperv/hyperv.c b/IPXE/ipxe-3fe683e/src/arch/x86/drivers/hyperv/hyperv.c
new file mode 100644 (file)
index 0000000..01e13c3
--- /dev/null
@@ -0,0 +1,820 @@
+/*
+ * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/** @file
+ *
+ * Hyper-V driver
+ *
+ */
+
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <byteswap.h>
+#include <pic8259.h>
+#include <ipxe/malloc.h>
+#include <ipxe/device.h>
+#include <ipxe/timer.h>
+#include <ipxe/quiesce.h>
+#include <ipxe/cpuid.h>
+#include <ipxe/msr.h>
+#include <ipxe/hyperv.h>
+#include <ipxe/vmbus.h>
+#include "hyperv.h"
+
+/** Maximum time to wait for a message response
+ *
+ * This is a policy decision.
+ */
+#define HV_MESSAGE_MAX_WAIT_MS 1000
+
+/** Hyper-V timer frequency (fixed 10Mhz) */
+#define HV_TIMER_HZ 10000000
+
+/** Hyper-V timer scale factor (used to avoid 64-bit division) */
+#define HV_TIMER_SHIFT 18
+
+/**
+ * Convert a Hyper-V status code to an iPXE status code
+ *
+ * @v status           Hyper-V status code
+ * @ret rc             iPXE status code (before negation)
+ */
+#define EHV( status ) EPLATFORM ( EINFO_EPLATFORM, (status) )
+
+/**
+ * Allocate zeroed pages
+ *
+ * @v hv               Hyper-V hypervisor
+ * @v ...              Page addresses to fill in, terminated by NULL
+ * @ret rc             Return status code
+ */
+__attribute__ (( sentinel )) int
+hv_alloc_pages ( struct hv_hypervisor *hv, ... ) {
+       va_list args;
+       void **page;
+       int i;
+
+       /* Allocate and zero pages */
+       va_start ( args, hv );
+       for ( i = 0 ; ( ( page = va_arg ( args, void ** ) ) != NULL ); i++ ) {
+               *page = malloc_dma ( PAGE_SIZE, PAGE_SIZE );
+               if ( ! *page )
+                       goto err_alloc;
+               memset ( *page, 0, PAGE_SIZE );
+       }
+       va_end ( args );
+
+       return 0;
+
+ err_alloc:
+       va_end ( args );
+       va_start ( args, hv );
+       for ( ; i >= 0 ; i-- ) {
+               page = va_arg ( args, void ** );
+               free_dma ( *page, PAGE_SIZE );
+       }
+       va_end ( args );
+       return -ENOMEM;
+}
+
+/**
+ * Free pages
+ *
+ * @v hv               Hyper-V hypervisor
+ * @v ...              Page addresses, terminated by NULL
+ */
+__attribute__ (( sentinel )) void
+hv_free_pages ( struct hv_hypervisor *hv, ... ) {
+       va_list args;
+       void *page;
+
+       va_start ( args, hv );
+       while ( ( page = va_arg ( args, void * ) ) != NULL )
+               free_dma ( page, PAGE_SIZE );
+       va_end ( args );
+}
+
+/**
+ * Allocate message buffer
+ *
+ * @v hv               Hyper-V hypervisor
+ * @ret rc             Return status code
+ */
+static int hv_alloc_message ( struct hv_hypervisor *hv ) {
+
+       /* Allocate buffer.  Must be aligned to at least 8 bytes and
+        * must not cross a page boundary, so align on its own size.
+        */
+       hv->message = malloc_dma ( sizeof ( *hv->message ),
+                                  sizeof ( *hv->message ) );
+       if ( ! hv->message )
+               return -ENOMEM;
+
+       return 0;
+}
+
+/**
+ * Free message buffer
+ *
+ * @v hv               Hyper-V hypervisor
+ */
+static void hv_free_message ( struct hv_hypervisor *hv ) {
+
+       /* Free buffer */
+       free_dma ( hv->message, sizeof ( *hv->message ) );
+}
+
+/**
+ * Check whether or not we are running in Hyper-V
+ *
+ * @ret rc             Return status code
+ */
+static int hv_check_hv ( void ) {
+       struct x86_features features;
+       uint32_t interface_id;
+       uint32_t discard_ebx;
+       uint32_t discard_ecx;
+       uint32_t discard_edx;
+
+       /* Check for presence of a hypervisor (not necessarily Hyper-V) */
+       x86_features ( &features );
+       if ( ! ( features.intel.ecx & CPUID_FEATURES_INTEL_ECX_HYPERVISOR ) ) {
+               DBGC ( HV_INTERFACE_ID, "HV not running in a hypervisor\n" );
+               return -ENODEV;
+       }
+
+       /* Check that hypervisor is Hyper-V */
+       cpuid ( HV_CPUID_INTERFACE_ID, 0, &interface_id, &discard_ebx,
+               &discard_ecx, &discard_edx );
+       if ( interface_id != HV_INTERFACE_ID ) {
+               DBGC ( HV_INTERFACE_ID, "HV not running in Hyper-V (interface "
+                      "ID %#08x)\n", interface_id );
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+/**
+ * Check required features
+ *
+ * @v hv               Hyper-V hypervisor
+ * @ret rc             Return status code
+ */
+static int hv_check_features ( struct hv_hypervisor *hv ) {
+       uint32_t available;
+       uint32_t permissions;
+       uint32_t discard_ecx;
+       uint32_t discard_edx;
+
+       /* Check that required features and privileges are available */
+       cpuid ( HV_CPUID_FEATURES, 0, &available, &permissions, &discard_ecx,
+               &discard_edx );
+       if ( ! ( available & HV_FEATURES_AVAIL_HYPERCALL_MSR ) ) {
+               DBGC ( hv, "HV %p has no hypercall MSRs (features %08x:%08x)\n",
+                      hv, available, permissions );
+               return -ENODEV;
+       }
+       if ( ! ( available & HV_FEATURES_AVAIL_SYNIC_MSR ) ) {
+               DBGC ( hv, "HV %p has no SynIC MSRs (features %08x:%08x)\n",
+                      hv, available, permissions );
+               return -ENODEV;
+       }
+       if ( ! ( permissions & HV_FEATURES_PERM_POST_MESSAGES ) ) {
+               DBGC ( hv, "HV %p cannot post messages (features %08x:%08x)\n",
+                      hv, available, permissions );
+               return -EACCES;
+       }
+       if ( ! ( permissions & HV_FEATURES_PERM_SIGNAL_EVENTS ) ) {
+               DBGC ( hv, "HV %p cannot signal events (features %08x:%08x)",
+                      hv, available, permissions );
+               return -EACCES;
+       }
+
+       return 0;
+}
+
+/**
+ * Check that Gen 2 UEFI firmware is not running
+ *
+ * @v hv               Hyper-V hypervisor
+ * @ret rc             Return status code
+ *
+ * We must not steal ownership from the Gen 2 UEFI firmware, since
+ * doing so will cause an immediate crash.  Avoid this by checking for
+ * the guest OS identity known to be used by the Gen 2 UEFI firmware.
+ */
+static int hv_check_uefi ( struct hv_hypervisor *hv ) {
+       uint64_t guest_os_id;
+
+       /* Check for UEFI firmware's guest OS identity */
+       guest_os_id = rdmsr ( HV_X64_MSR_GUEST_OS_ID );
+       if ( guest_os_id == HV_GUEST_OS_ID_UEFI ) {
+               DBGC ( hv, "HV %p is owned by UEFI firmware\n", hv );
+               return -ENOTSUP;
+       }
+
+       return 0;
+}
+
+/**
+ * Map hypercall page
+ *
+ * @v hv               Hyper-V hypervisor
+ */
+static void hv_map_hypercall ( struct hv_hypervisor *hv ) {
+       union {
+               struct {
+                       uint32_t ebx;
+                       uint32_t ecx;
+                       uint32_t edx;
+               } __attribute__ (( packed ));
+               char text[ 13 /* "bbbbccccdddd" + NUL */ ];
+       } vendor_id;
+       uint32_t build;
+       uint32_t version;
+       uint32_t discard_eax;
+       uint32_t discard_ecx;
+       uint32_t discard_edx;
+       uint64_t guest_os_id;
+       uint64_t hypercall;
+
+       /* Report guest OS identity */
+       guest_os_id = rdmsr ( HV_X64_MSR_GUEST_OS_ID );
+       if ( guest_os_id != 0 ) {
+               DBGC ( hv, "HV %p guest OS ID MSR was %#08llx\n",
+                      hv, guest_os_id );
+       }
+       guest_os_id = HV_GUEST_OS_ID_IPXE;
+       DBGC2 ( hv, "HV %p guest OS ID MSR is %#08llx\n", hv, guest_os_id );
+       wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id );
+
+       /* Get hypervisor system identity (for debugging) */
+       cpuid ( HV_CPUID_VENDOR_ID, 0, &discard_eax, &vendor_id.ebx,
+               &vendor_id.ecx, &vendor_id.edx );
+       vendor_id.text[ sizeof ( vendor_id.text ) - 1 ] = '\0';
+       cpuid ( HV_CPUID_HYPERVISOR_ID, 0, &build, &version, &discard_ecx,
+               &discard_edx );
+       DBGC ( hv, "HV %p detected \"%s\" version %d.%d build %d\n", hv,
+              vendor_id.text, ( version >> 16 ), ( version & 0xffff ), build );
+
+       /* Map hypercall page */
+       hypercall = rdmsr ( HV_X64_MSR_HYPERCALL );
+       hypercall &= ( PAGE_SIZE - 1 );
+       hypercall |= ( virt_to_phys ( hv->hypercall ) | HV_HYPERCALL_ENABLE );
+       DBGC2 ( hv, "HV %p hypercall MSR is %#08llx\n", hv, hypercall );
+       wrmsr ( HV_X64_MSR_HYPERCALL, hypercall );
+}
+
+/**
+ * Unmap hypercall page
+ *
+ * @v hv               Hyper-V hypervisor
+ */
+static void hv_unmap_hypercall ( struct hv_hypervisor *hv ) {
+       uint64_t hypercall;
+       uint64_t guest_os_id;
+
+       /* Unmap the hypercall page */
+       hypercall = rdmsr ( HV_X64_MSR_HYPERCALL );
+       hypercall &= ( ( PAGE_SIZE - 1 ) & ~HV_HYPERCALL_ENABLE );
+       DBGC2 ( hv, "HV %p hypercall MSR is %#08llx\n", hv, hypercall );
+       wrmsr ( HV_X64_MSR_HYPERCALL, hypercall );
+
+       /* Reset the guest OS identity */
+       guest_os_id = 0;
+       DBGC2 ( hv, "HV %p guest OS ID MSR is %#08llx\n", hv, guest_os_id );
+       wrmsr ( HV_X64_MSR_GUEST_OS_ID, guest_os_id );
+}
+
+/**
+ * Map synthetic interrupt controller
+ *
+ * @v hv               Hyper-V hypervisor
+ */
+static void hv_map_synic ( struct hv_hypervisor *hv ) {
+       uint64_t simp;
+       uint64_t siefp;
+       uint64_t scontrol;
+
+       /* Zero SynIC message and event pages */
+       memset ( hv->synic.message, 0, PAGE_SIZE );
+       memset ( hv->synic.event, 0, PAGE_SIZE );
+
+       /* Map SynIC message page */
+       simp = rdmsr ( HV_X64_MSR_SIMP );
+       simp &= ( PAGE_SIZE - 1 );
+       simp |= ( virt_to_phys ( hv->synic.message ) | HV_SIMP_ENABLE );
+       DBGC2 ( hv, "HV %p SIMP MSR is %#08llx\n", hv, simp );
+       wrmsr ( HV_X64_MSR_SIMP, simp );
+
+       /* Map SynIC event page */
+       siefp = rdmsr ( HV_X64_MSR_SIEFP );
+       siefp &= ( PAGE_SIZE - 1 );
+       siefp |= ( virt_to_phys ( hv->synic.event ) | HV_SIEFP_ENABLE );
+       DBGC2 ( hv, "HV %p SIEFP MSR is %#08llx\n", hv, siefp );
+       wrmsr ( HV_X64_MSR_SIEFP, siefp );
+
+       /* Enable SynIC */
+       scontrol = rdmsr ( HV_X64_MSR_SCONTROL );
+       scontrol |= HV_SCONTROL_ENABLE;
+       DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol );
+       wrmsr ( HV_X64_MSR_SCONTROL, scontrol );
+}
+
+/**
+ * Unmap synthetic interrupt controller, leaving SCONTROL untouched
+ *
+ * @v hv               Hyper-V hypervisor
+ */
+static void hv_unmap_synic_no_scontrol ( struct hv_hypervisor *hv ) {
+       uint64_t siefp;
+       uint64_t simp;
+
+       /* Unmap SynIC event page */
+       siefp = rdmsr ( HV_X64_MSR_SIEFP );
+       siefp &= ( ( PAGE_SIZE - 1 ) & ~HV_SIEFP_ENABLE );
+       DBGC2 ( hv, "HV %p SIEFP MSR is %#08llx\n", hv, siefp );
+       wrmsr ( HV_X64_MSR_SIEFP, siefp );
+
+       /* Unmap SynIC message page */
+       simp = rdmsr ( HV_X64_MSR_SIMP );
+       simp &= ( ( PAGE_SIZE - 1 ) & ~HV_SIMP_ENABLE );
+       DBGC2 ( hv, "HV %p SIMP MSR is %#08llx\n", hv, simp );
+       wrmsr ( HV_X64_MSR_SIMP, simp );
+}
+
+/**
+ * Unmap synthetic interrupt controller
+ *
+ * @v hv               Hyper-V hypervisor
+ */
+static void hv_unmap_synic ( struct hv_hypervisor *hv ) {
+       uint64_t scontrol;
+
+       /* Disable SynIC */
+       scontrol = rdmsr ( HV_X64_MSR_SCONTROL );
+       scontrol &= ~HV_SCONTROL_ENABLE;
+       DBGC2 ( hv, "HV %p SCONTROL MSR is %#08llx\n", hv, scontrol );
+       wrmsr ( HV_X64_MSR_SCONTROL, scontrol );
+
+       /* Unmap SynIC event and message pages */
+       hv_unmap_synic_no_scontrol ( hv );
+}
+
+/**
+ * Enable synthetic interrupt
+ *
+ * @v hv               Hyper-V hypervisor
+ * @v sintx            Synthetic interrupt number
+ */
+void hv_enable_sint ( struct hv_hypervisor *hv, unsigned int sintx ) {
+       unsigned long msr = HV_X64_MSR_SINT ( sintx );
+       uint64_t sint;
+
+       /* Enable synthetic interrupt
+        *
+        * We have to enable the interrupt, otherwise messages will
+        * not be delivered (even though the documentation implies
+        * that polling for messages is possible).  We enable AutoEOI
+        * and hook the interrupt to the obsolete IRQ13 (FPU
+        * exception) vector, which will be implemented as a no-op.
+        */
+       sint = rdmsr ( msr );
+       sint &= ~( HV_SINT_MASKED | HV_SINT_VECTOR_MASK );
+       sint |= ( HV_SINT_AUTO_EOI |
+                 HV_SINT_VECTOR ( IRQ_INT ( 13 /* See comment above */ ) ) );
+       DBGC2 ( hv, "HV %p SINT%d MSR is %#08llx\n", hv, sintx, sint );
+       wrmsr ( msr, sint );
+}
+
+/**
+ * Disable synthetic interrupt
+ *
+ * @v hv               Hyper-V hypervisor
+ * @v sintx            Synthetic interrupt number
+ */
+void hv_disable_sint ( struct hv_hypervisor *hv, unsigned int sintx ) {
+       unsigned long msr = HV_X64_MSR_SINT ( sintx );
+       uint64_t sint;
+
+       /* Do nothing if interrupt is already disabled */
+       sint = rdmsr ( msr );
+       if ( sint & HV_SINT_MASKED )
+               return;
+
+       /* Disable synthetic interrupt */
+       sint &= ~HV_SINT_AUTO_EOI;
+       sint |= HV_SINT_MASKED;
+       DBGC2 ( hv, "HV %p SINT%d MSR is %#08llx\n", hv, sintx, sint );
+       wrmsr ( msr, sint );
+}
+
+/**
+ * Post message
+ *
+ * @v hv               Hyper-V hypervisor
+ * @v id               Connection ID
+ * @v type             Message type
+ * @v data             Message
+ * @v len              Length of message
+ * @ret rc             Return status code
+ */
+int hv_post_message ( struct hv_hypervisor *hv, unsigned int id,
+                     unsigned int type, const void *data, size_t len ) {
+       struct hv_post_message *msg = &hv->message->posted;
+       int status;
+       int rc;
+
+       /* Sanity check */
+       assert ( len <= sizeof ( msg->data ) );
+
+       /* Construct message */
+       memset ( msg, 0, sizeof ( *msg ) );
+       msg->id = cpu_to_le32 ( id );
+       msg->type = cpu_to_le32 ( type );
+       msg->len = cpu_to_le32 ( len );
+       memcpy ( msg->data, data, len );
+       DBGC2 ( hv, "HV %p connection %d posting message type %#08x:\n",
+               hv, id, type );
+       DBGC2_HDA ( hv, 0, msg->data, len );
+
+       /* Post message */
+       if ( ( status = hv_call ( hv, HV_POST_MESSAGE, msg, NULL ) ) != 0 ) {
+               rc = -EHV ( status );
+               DBGC ( hv, "HV %p could not post message to %#08x: %s\n",
+                      hv, id, strerror ( rc ) );
+               return rc;
+       }
+
+       return 0;
+}
+
+/**
+ * Wait for received message
+ *
+ * @v hv               Hyper-V hypervisor
+ * @v sintx            Synthetic interrupt number
+ * @ret rc             Return status code
+ */
+int hv_wait_for_message ( struct hv_hypervisor *hv, unsigned int sintx ) {
+       struct hv_message *msg = &hv->message->received;
+       struct hv_message *src = &hv->synic.message[sintx];
+       unsigned int retries;
+       size_t len;
+
+       /* Wait for message to arrive */
+       for ( retries = 0 ; retries < HV_MESSAGE_MAX_WAIT_MS ; retries++ ) {
+
+               /* Check for message */
+               if ( src->type ) {
+
+                       /* Copy message */
+                       memset ( msg, 0, sizeof ( *msg ) );
+                       len = src->len;
+                       assert ( len <= sizeof ( *msg ) );
+                       memcpy ( msg, src,
+                                ( offsetof ( typeof ( *msg ), data ) + len ) );
+                       DBGC2 ( hv, "HV %p SINT%d received message type "
+                               "%#08x:\n", hv, sintx,
+                               le32_to_cpu ( msg->type ) );
+                       DBGC2_HDA ( hv, 0, msg->data, len );
+
+                       /* Consume message */
+                       src->type = 0;
+
+                       return 0;
+               }
+
+               /* Trigger message delivery */
+               wrmsr ( HV_X64_MSR_EOM, 0 );
+
+               /* Delay */
+               mdelay ( 1 );
+       }
+
+       DBGC ( hv, "HV %p SINT%d timed out waiting for message\n",
+              hv, sintx );
+       return -ETIMEDOUT;
+}
+
+/**
+ * Signal event
+ *
+ * @v hv               Hyper-V hypervisor
+ * @v id               Connection ID
+ * @v flag             Flag number
+ * @ret rc             Return status code
+ */
+int hv_signal_event ( struct hv_hypervisor *hv, unsigned int id,
+                     unsigned int flag ) {
+       struct hv_signal_event *event = &hv->message->signalled;
+       int status;
+       int rc;
+
+       /* Construct event */
+       memset ( event, 0, sizeof ( *event ) );
+       event->id = cpu_to_le32 ( id );
+       event->flag = cpu_to_le16 ( flag );
+
+       /* Signal event */
+       if ( ( status = hv_call ( hv, HV_SIGNAL_EVENT, event, NULL ) ) != 0 ) {
+               rc = -EHV ( status );
+               DBGC ( hv, "HV %p could not signal event to %#08x: %s\n",
+                      hv, id, strerror ( rc ) );
+               return rc;
+       }
+
+       return 0;
+}
+
+/**
+ * Probe root device
+ *
+ * @v rootdev          Root device
+ * @ret rc             Return status code
+ */
+static int hv_probe ( struct root_device *rootdev ) {
+       struct hv_hypervisor *hv;
+       int rc;
+
+       /* Check we are running in Hyper-V */
+       if ( ( rc = hv_check_hv() ) != 0 )
+               goto err_check_hv;
+
+       /* Allocate and initialise structure */
+       hv = zalloc ( sizeof ( *hv ) );
+       if ( ! hv ) {
+               rc = -ENOMEM;
+               goto err_alloc;
+       }
+
+       /* Check features */
+       if ( ( rc = hv_check_features ( hv ) ) != 0 )
+               goto err_check_features;
+
+       /* Check that Gen 2 UEFI firmware is not running */
+       if ( ( rc = hv_check_uefi ( hv ) ) != 0 )
+               goto err_check_uefi;
+
+       /* Allocate pages */
+       if ( ( rc = hv_alloc_pages ( hv, &hv->hypercall, &hv->synic.message,
+                                    &hv->synic.event, NULL ) ) != 0 )
+               goto err_alloc_pages;
+
+       /* Allocate message buffer */
+       if ( ( rc = hv_alloc_message ( hv ) ) != 0 )
+               goto err_alloc_message;
+
+       /* Map hypercall page */
+       hv_map_hypercall ( hv );
+
+       /* Map synthetic interrupt controller */
+       hv_map_synic ( hv );
+
+       /* Probe Hyper-V devices */
+       if ( ( rc = vmbus_probe ( hv, &rootdev->dev ) ) != 0 )
+               goto err_vmbus_probe;
+
+       rootdev_set_drvdata ( rootdev, hv );
+       return 0;
+
+       vmbus_remove ( hv, &rootdev->dev );
+ err_vmbus_probe:
+       hv_unmap_synic ( hv );
+       hv_unmap_hypercall ( hv );
+       hv_free_message ( hv );
+ err_alloc_message:
+       hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event,
+                       NULL );
+ err_alloc_pages:
+ err_check_uefi:
+ err_check_features:
+       free ( hv );
+ err_alloc:
+ err_check_hv:
+       return rc;
+}
+
+/**
+ * Remove root device
+ *
+ * @v rootdev          Root device
+ */
+static void hv_remove ( struct root_device *rootdev ) {
+       struct hv_hypervisor *hv = rootdev_get_drvdata ( rootdev );
+
+       vmbus_remove ( hv, &rootdev->dev );
+       hv_unmap_synic ( hv );
+       hv_unmap_hypercall ( hv );
+       hv_free_message ( hv );
+       hv_free_pages ( hv, hv->hypercall, hv->synic.message, hv->synic.event,
+                       NULL );
+       free ( hv );
+       rootdev_set_drvdata ( rootdev, NULL );
+}
+
+/** Hyper-V root device driver */
+static struct root_driver hv_root_driver = {
+       .probe = hv_probe,
+       .remove = hv_remove,
+};
+
+/** Hyper-V root device */
+struct root_device hv_root_device __root_device = {
+       .dev = { .name = "Hyper-V" },
+       .driver = &hv_root_driver,
+};
+
+/**
+ * Quiesce system
+ *
+ */
+static void hv_quiesce ( void ) {
+       struct hv_hypervisor *hv = rootdev_get_drvdata ( &hv_root_device );
+       unsigned int i;
+
+       /* Do nothing if we are not running in Hyper-V */
+       if ( ! hv )
+               return;
+
+       /* The "enlightened" portions of the Windows Server 2016 boot
+        * process will not cleanly take ownership of an active
+        * Hyper-V connection.  Experimentation shows that the minimum
+        * requirement is that we disable the SynIC message page
+        * (i.e. zero the SIMP MSR).
+        *
+        * We cannot perform a full shutdown of the Hyper-V
+        * connection.  Experimentation shows that if we disable the
+        * SynIC (i.e. zero the SCONTROL MSR) then Windows Server 2016
+        * will enter an indefinite wait loop.
+        *
+        * Attempt to create a safe handover environment by resetting
+        * all MSRs except for SCONTROL.
+        *
+        * Note that we do not shut down our VMBus devices, since we
+        * may need to unquiesce the system and continue operation.
+        */
+
+       /* Disable all synthetic interrupts */
+       for ( i = 0 ; i <= HV_SINT_MAX ; i++ )
+               hv_disable_sint ( hv, i );
+
+       /* Unmap synthetic interrupt controller, leaving SCONTROL
+        * enabled (see above).
+        */
+       hv_unmap_synic_no_scontrol ( hv );
+
+       /* Unmap hypercall page */
+       hv_unmap_hypercall ( hv );
+
+       DBGC ( hv, "HV %p quiesced\n", hv );
+}
+
+/**
+ * Unquiesce system
+ *
+ */
+static void hv_unquiesce ( void ) {
+       struct hv_hypervisor *hv = rootdev_get_drvdata ( &hv_root_device );
+       uint64_t simp;
+       int rc;
+
+       /* Do nothing if we are not running in Hyper-V */
+       if ( ! hv )
+               return;
+
+       /* Experimentation shows that the "enlightened" portions of
+        * Windows Server 2016 will break our Hyper-V connection at
+        * some point during a SAN boot.  Surprisingly it does not
+        * change the guest OS ID MSR, but it does leave the SynIC
+        * message page disabled.
+        *
+        * Our own explicit quiescing procedure will also disable the
+        * SynIC message page.  We can therefore use the SynIC message
+        * page enable bit as a heuristic to determine when we need to
+        * reestablish our Hyper-V connection.
+        */
+       simp = rdmsr ( HV_X64_MSR_SIMP );
+       if ( simp & HV_SIMP_ENABLE )
+               return;
+
+       /* Remap hypercall page */
+       hv_map_hypercall ( hv );
+
+       /* Remap synthetic interrupt controller */
+       hv_map_synic ( hv );
+
+       /* Reset Hyper-V devices */
+       if ( ( rc = vmbus_reset ( hv, &hv_root_device.dev ) ) != 0 ) {
+               DBGC ( hv, "HV %p could not unquiesce: %s\n",
+                      hv, strerror ( rc ) );
+               /* Nothing we can do */
+               return;
+       }
+}
+
+/** Hyper-V quiescer */
+struct quiescer hv_quiescer __quiescer = {
+       .quiesce = hv_quiesce,
+       .unquiesce = hv_unquiesce,
+};
+
+/**
+ * Probe timer
+ *
+ * @ret rc             Return status code
+ */
+static int hv_timer_probe ( void ) {
+       uint32_t available;
+       uint32_t discard_ebx;
+       uint32_t discard_ecx;
+       uint32_t discard_edx;
+       int rc;
+
+       /* Check we are running in Hyper-V */
+       if ( ( rc = hv_check_hv() ) != 0 )
+               return rc;
+
+       /* Check for available reference counter */
+       cpuid ( HV_CPUID_FEATURES, 0, &available, &discard_ebx, &discard_ecx,
+               &discard_edx );
+       if ( ! ( available & HV_FEATURES_AVAIL_TIME_REF_COUNT_MSR ) ) {
+               DBGC ( HV_INTERFACE_ID, "HV has no time reference counter\n" );
+               return -ENODEV;
+       }
+
+       return 0;
+}
+
+/**
+ * Get current system time in ticks
+ *
+ * @ret ticks          Current time, in ticks
+ */
+static unsigned long hv_currticks ( void ) {
+
+       /* Calculate time using a combination of bit shifts and
+        * multiplication (to avoid a 64-bit division).
+        */
+       return ( ( rdmsr ( HV_X64_MSR_TIME_REF_COUNT ) >> HV_TIMER_SHIFT ) *
+                ( TICKS_PER_SEC / ( HV_TIMER_HZ >> HV_TIMER_SHIFT ) ) );
+}
+
+/**
+ * Delay for a fixed number of microseconds
+ *
+ * @v usecs            Number of microseconds for which to delay
+ */
+static void hv_udelay ( unsigned long usecs ) {
+       uint32_t start;
+       uint32_t elapsed;
+       uint32_t threshold;
+
+       /* Spin until specified number of 10MHz ticks have elapsed */
+       start = rdmsr ( HV_X64_MSR_TIME_REF_COUNT );
+       threshold = ( usecs * ( HV_TIMER_HZ / 1000000 ) );
+       do {
+               elapsed = ( rdmsr ( HV_X64_MSR_TIME_REF_COUNT ) - start );
+       } while ( elapsed < threshold );
+}
+
+/** Hyper-V timer */
+struct timer hv_timer __timer ( TIMER_PREFERRED ) = {
+       .name = "Hyper-V",
+       .probe = hv_timer_probe,
+       .currticks = hv_currticks,
+       .udelay = hv_udelay,
+};
+
+/* Drag in objects via hv_root_device */
+REQUIRING_SYMBOL ( hv_root_device );
+
+/* Drag in netvsc driver */
+//REQUIRE_OBJECT ( netvsc );
diff --git a/IPXE/ipxe-3fe683e/src/arch/x86/drivers/xen/hvm.c b/IPXE/ipxe-3fe683e/src/arch/x86/drivers/xen/hvm.c
new file mode 100644 (file)
index 0000000..1d9188e
--- /dev/null
@@ -0,0 +1,503 @@
+/*
+ * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdint.h>
+#include <stdio.h>
+#include <errno.h>
+#include <ipxe/malloc.h>
+#include <ipxe/pci.h>
+#include <ipxe/cpuid.h>
+#include <ipxe/msr.h>
+#include <ipxe/xen.h>
+#include <ipxe/xenver.h>
+#include <ipxe/xenmem.h>
+#include <ipxe/xenstore.h>
+#include <ipxe/xenbus.h>
+#include <ipxe/xengrant.h>
+#include "hvm.h"
+
+/** @file
+ *
+ * Xen HVM driver
+ *
+ */
+
+/**
+ * Get CPUID base
+ *
+ * @v hvm              HVM device
+ * @ret rc             Return status code
+ */
+static int hvm_cpuid_base ( struct hvm_device *hvm ) {
+       struct {
+               uint32_t ebx;
+               uint32_t ecx;
+               uint32_t edx;
+       } __attribute__ (( packed )) signature;
+       uint32_t base;
+       uint32_t version;
+       uint32_t discard_eax;
+       uint32_t discard_ebx;
+       uint32_t discard_ecx;
+       uint32_t discard_edx;
+
+       /* Scan for magic signature */
+       for ( base = HVM_CPUID_MIN ; base <= HVM_CPUID_MAX ;
+             base += HVM_CPUID_STEP ) {
+               cpuid ( base, 0, &discard_eax, &signature.ebx, &signature.ecx,
+                       &signature.edx );
+               if ( memcmp ( &signature, HVM_CPUID_MAGIC,
+                             sizeof ( signature ) ) == 0 ) {
+                       hvm->cpuid_base = base;
+                       cpuid ( ( base + HVM_CPUID_VERSION ), 0, &version,
+                               &discard_ebx, &discard_ecx, &discard_edx );
+                       DBGC2 ( hvm, "HVM using CPUID base %#08x (v%d.%d)\n",
+                               base, ( version >> 16 ), ( version & 0xffff ) );
+                       return 0;
+               }
+       }
+
+       DBGC ( hvm, "HVM could not find hypervisor\n" );
+       return -ENODEV;
+}
+
+/**
+ * Map hypercall page(s)
+ *
+ * @v hvm              HVM device
+ * @ret rc             Return status code
+ */
+static int hvm_map_hypercall ( struct hvm_device *hvm ) {
+       uint32_t pages;
+       uint32_t msr;
+       uint32_t discard_ecx;
+       uint32_t discard_edx;
+       physaddr_t hypercall_phys;
+       uint32_t version;
+       static xen_extraversion_t extraversion;
+       int xenrc;
+       int rc;
+
+       /* Get number of hypercall pages and MSR to use */
+       cpuid ( ( hvm->cpuid_base + HVM_CPUID_PAGES ), 0, &pages, &msr,
+               &discard_ecx, &discard_edx );
+
+       /* Allocate pages */
+       hvm->hypercall_len = ( pages * PAGE_SIZE );
+       hvm->xen.hypercall = malloc_dma ( hvm->hypercall_len, PAGE_SIZE );
+       if ( ! hvm->xen.hypercall ) {
+               DBGC ( hvm, "HVM could not allocate %d hypercall page(s)\n",
+                      pages );
+               return -ENOMEM;
+       }
+       hypercall_phys = virt_to_phys ( hvm->xen.hypercall );
+       DBGC2 ( hvm, "HVM hypercall page(s) at [%#08lx,%#08lx) via MSR %#08x\n",
+               hypercall_phys, ( hypercall_phys + hvm->hypercall_len ), msr );
+
+       /* Write to MSR */
+       wrmsr ( msr, hypercall_phys );
+
+       /* Check that hypercall mechanism is working */
+       version = xenver_version ( &hvm->xen );
+       if ( ( xenrc = xenver_extraversion ( &hvm->xen, &extraversion ) ) != 0){
+               rc = -EXEN ( xenrc );
+               DBGC ( hvm, "HVM could not get extraversion: %s\n",
+                      strerror ( rc ) );
+               return rc;
+       }
+       DBGC2 ( hvm, "HVM found Xen version %d.%d%s\n",
+               ( version >> 16 ), ( version & 0xffff ) , extraversion );
+
+       return 0;
+}
+
+/**
+ * Unmap hypercall page(s)
+ *
+ * @v hvm              HVM device
+ */
+static void hvm_unmap_hypercall ( struct hvm_device *hvm ) {
+
+       /* Free pages */
+       free_dma ( hvm->xen.hypercall, hvm->hypercall_len );
+}
+
+/**
+ * Allocate and map MMIO space
+ *
+ * @v hvm              HVM device
+ * @v space            Source mapping space
+ * @v len              Length (must be a multiple of PAGE_SIZE)
+ * @ret mmio           MMIO space address, or NULL on error
+ */
+static void * hvm_ioremap ( struct hvm_device *hvm, unsigned int space,
+                           size_t len ) {
+       struct xen_add_to_physmap add;
+       struct xen_remove_from_physmap remove;
+       unsigned int pages = ( len / PAGE_SIZE );
+       physaddr_t mmio_phys;
+       unsigned int i;
+       void *mmio;
+       int xenrc;
+       int rc;
+
+       /* Sanity check */
+       assert ( ( len % PAGE_SIZE ) == 0 );
+
+       /* Check for available space */
+       if ( ( hvm->mmio_offset + len ) > hvm->mmio_len ) {
+               DBGC ( hvm, "HVM could not allocate %zd bytes of MMIO space "
+                      "(%zd of %zd remaining)\n", len,
+                      ( hvm->mmio_len - hvm->mmio_offset ), hvm->mmio_len );
+               goto err_no_space;
+       }
+
+       /* Map this space */
+       mmio = ioremap ( ( hvm->mmio + hvm->mmio_offset ), len );
+       if ( ! mmio ) {
+               DBGC ( hvm, "HVM could not map MMIO space [%08lx,%08lx)\n",
+                      ( hvm->mmio + hvm->mmio_offset ),
+                      ( hvm->mmio + hvm->mmio_offset + len ) );
+               goto err_ioremap;
+       }
+       mmio_phys = virt_to_phys ( mmio );
+
+       /* Add to physical address space */
+       for ( i = 0 ; i < pages ; i++ ) {
+               add.domid = DOMID_SELF;
+               add.idx = i;
+               add.space = space;
+               add.gpfn = ( ( mmio_phys / PAGE_SIZE ) + i );
+               if ( ( xenrc = xenmem_add_to_physmap ( &hvm->xen, &add ) ) !=0){
+                       rc = -EXEN ( xenrc );
+                       DBGC ( hvm, "HVM could not add space %d idx %d at "
+                              "[%08lx,%08lx): %s\n", space, i,
+                              ( mmio_phys + ( i * PAGE_SIZE ) ),
+                              ( mmio_phys + ( ( i + 1 ) * PAGE_SIZE ) ),
+                              strerror ( rc ) );
+                       goto err_add_to_physmap;
+               }
+       }
+
+       /* Update offset */
+       hvm->mmio_offset += len;
+
+       return mmio;
+
+       i = pages;
+ err_add_to_physmap:
+       for ( i-- ; ( signed int ) i >= 0 ; i-- ) {
+               remove.domid = DOMID_SELF;
+               add.gpfn = ( ( mmio_phys / PAGE_SIZE ) + i );
+               xenmem_remove_from_physmap ( &hvm->xen, &remove );
+       }
+       iounmap ( mmio );
+ err_ioremap:
+ err_no_space:
+       return NULL;
+}
+
+/**
+ * Unmap MMIO space
+ *
+ * @v hvm              HVM device
+ * @v mmio             MMIO space address
+ * @v len              Length (must be a multiple of PAGE_SIZE)
+ */
+static void hvm_iounmap ( struct hvm_device *hvm, void *mmio, size_t len ) {
+       struct xen_remove_from_physmap remove;
+       physaddr_t mmio_phys = virt_to_phys ( mmio );
+       unsigned int pages = ( len / PAGE_SIZE );
+       unsigned int i;
+       int xenrc;
+       int rc;
+
+       /* Unmap this space */
+       iounmap ( mmio );
+
+       /* Remove from physical address space */
+       for ( i = 0 ; i < pages ; i++ ) {
+               remove.domid = DOMID_SELF;
+               remove.gpfn = ( ( mmio_phys / PAGE_SIZE ) + i );
+               if ( ( xenrc = xenmem_remove_from_physmap ( &hvm->xen,
+                                                           &remove ) ) != 0 ) {
+                       rc = -EXEN ( xenrc );
+                       DBGC ( hvm, "HVM could not remove space [%08lx,%08lx): "
+                              "%s\n", ( mmio_phys + ( i * PAGE_SIZE ) ),
+                              ( mmio_phys + ( ( i + 1 ) * PAGE_SIZE ) ),
+                              strerror ( rc ) );
+                       /* Nothing we can do about this */
+               }
+       }
+}
+
+/**
+ * Map shared info page
+ *
+ * @v hvm              HVM device
+ * @ret rc             Return status code
+ */
+static int hvm_map_shared_info ( struct hvm_device *hvm ) {
+       physaddr_t shared_info_phys;
+       int rc;
+
+       /* Map shared info page */
+       hvm->xen.shared = hvm_ioremap ( hvm, XENMAPSPACE_shared_info,
+                                       PAGE_SIZE );
+       if ( ! hvm->xen.shared ) {
+               rc = -ENOMEM;
+               goto err_alloc;
+       }
+       shared_info_phys = virt_to_phys ( hvm->xen.shared );
+       DBGC2 ( hvm, "HVM shared info page at [%#08lx,%#08lx)\n",
+               shared_info_phys, ( shared_info_phys + PAGE_SIZE ) );
+
+       /* Sanity check */
+       DBGC2 ( hvm, "HVM wallclock time is %d\n",
+               readl ( &hvm->xen.shared->wc_sec ) );
+
+       return 0;
+
+       hvm_iounmap ( hvm, hvm->xen.shared, PAGE_SIZE );
+ err_alloc:
+       return rc;
+}
+
+/**
+ * Unmap shared info page
+ *
+ * @v hvm              HVM device
+ */
+static void hvm_unmap_shared_info ( struct hvm_device *hvm ) {
+
+       /* Unmap shared info page */
+       hvm_iounmap ( hvm, hvm->xen.shared, PAGE_SIZE );
+}
+
+/**
+ * Map grant table
+ *
+ * @v hvm              HVM device
+ * @ret rc             Return status code
+ */
+static int hvm_map_grant ( struct hvm_device *hvm ) {
+       physaddr_t grant_phys;
+       int rc;
+
+       /* Initialise grant table */
+       if ( ( rc = xengrant_init ( &hvm->xen ) ) != 0 ) {
+               DBGC ( hvm, "HVM could not initialise grant table: %s\n",
+                      strerror ( rc ) );
+               return rc;
+       }
+
+       /* Map grant table */
+       hvm->xen.grant.table = hvm_ioremap ( hvm, XENMAPSPACE_grant_table,
+                                            hvm->xen.grant.len );
+       if ( ! hvm->xen.grant.table )
+               return -ENODEV;
+
+       grant_phys = virt_to_phys ( hvm->xen.grant.table );
+       DBGC2 ( hvm, "HVM mapped grant table at [%08lx,%08lx)\n",
+               grant_phys, ( grant_phys + hvm->xen.grant.len ) );
+       return 0;
+}
+
+/**
+ * Unmap grant table
+ *
+ * @v hvm              HVM device
+ */
+static void hvm_unmap_grant ( struct hvm_device *hvm ) {
+
+       /* Unmap grant table */
+       hvm_iounmap ( hvm, hvm->xen.grant.table, hvm->xen.grant.len );
+}
+
+/**
+ * Map XenStore
+ *
+ * @v hvm              HVM device
+ * @ret rc             Return status code
+ */
+static int hvm_map_xenstore ( struct hvm_device *hvm ) {
+       uint64_t xenstore_evtchn;
+       uint64_t xenstore_pfn;
+       physaddr_t xenstore_phys;
+       char *name;
+       int xenrc;
+       int rc;
+
+       /* Get XenStore event channel */
+       if ( ( xenrc = xen_hvm_get_param ( &hvm->xen, HVM_PARAM_STORE_EVTCHN,
+                                          &xenstore_evtchn ) ) != 0 ) {
+               rc = -EXEN ( xenrc );
+               DBGC ( hvm, "HVM could not get XenStore event channel: %s\n",
+                      strerror ( rc ) );
+               return rc;
+       }
+       hvm->xen.store.port = xenstore_evtchn;
+
+       /* Get XenStore PFN */
+       if ( ( xenrc = xen_hvm_get_param ( &hvm->xen, HVM_PARAM_STORE_PFN,
+                                          &xenstore_pfn ) ) != 0 ) {
+               rc = -EXEN ( xenrc );
+               DBGC ( hvm, "HVM could not get XenStore PFN: %s\n",
+                      strerror ( rc ) );
+               return rc;
+       }
+       xenstore_phys = ( xenstore_pfn * PAGE_SIZE );
+
+       /* Map XenStore */
+       hvm->xen.store.intf = ioremap ( xenstore_phys, PAGE_SIZE );
+       if ( ! hvm->xen.store.intf ) {
+               DBGC ( hvm, "HVM could not map XenStore at [%08lx,%08lx)\n",
+                      xenstore_phys, ( xenstore_phys + PAGE_SIZE ) );
+               return -ENODEV;
+       }
+       DBGC2 ( hvm, "HVM mapped XenStore at [%08lx,%08lx) with event port "
+               "%d\n", xenstore_phys, ( xenstore_phys + PAGE_SIZE ),
+               hvm->xen.store.port );
+
+       /* Check that XenStore is working */
+       if ( ( rc = xenstore_read ( &hvm->xen, &name, "name", NULL ) ) != 0 ) {
+               DBGC ( hvm, "HVM could not read domain name: %s\n",
+                      strerror ( rc ) );
+               return rc;
+       }
+       DBGC2 ( hvm, "HVM running in domain \"%s\"\n", name );
+       free ( name );
+
+       return 0;
+}
+
+/**
+ * Unmap XenStore
+ *
+ * @v hvm              HVM device
+ */
+static void hvm_unmap_xenstore ( struct hvm_device *hvm ) {
+
+       /* Unmap XenStore */
+       iounmap ( hvm->xen.store.intf );
+}
+
+/**
+ * Probe PCI device
+ *
+ * @v pci              PCI device
+ * @ret rc             Return status code
+ */
+static int hvm_probe ( struct pci_device *pci ) {
+       struct hvm_device *hvm;
+       int rc;
+
+       /* Allocate and initialise structure */
+       hvm = zalloc ( sizeof ( *hvm ) );
+       if ( ! hvm ) {
+               rc = -ENOMEM;
+               goto err_alloc;
+       }
+       hvm->mmio = pci_bar_start ( pci, HVM_MMIO_BAR );
+       hvm->mmio_len = pci_bar_size ( pci, HVM_MMIO_BAR );
+       DBGC2 ( hvm, "HVM has MMIO space [%08lx,%08lx)\n",
+               hvm->mmio, ( hvm->mmio + hvm->mmio_len ) );
+
+       /* Fix up PCI device */
+       adjust_pci_device ( pci );
+
+       /* Attach to hypervisor */
+       if ( ( rc = hvm_cpuid_base ( hvm ) ) != 0 )
+               goto err_cpuid_base;
+       if ( ( rc = hvm_map_hypercall ( hvm ) ) != 0 )
+               goto err_map_hypercall;
+       if ( ( rc = hvm_map_shared_info ( hvm ) ) != 0 )
+               goto err_map_shared_info;
+       if ( ( rc = hvm_map_grant ( hvm ) ) != 0 )
+               goto err_map_grant;
+       if ( ( rc = hvm_map_xenstore ( hvm ) ) != 0 )
+               goto err_map_xenstore;
+
+       /* Probe Xen devices */
+       if ( ( rc = xenbus_probe ( &hvm->xen, &pci->dev ) ) != 0 ) {
+               DBGC ( hvm, "HVM could not probe Xen bus: %s\n",
+                      strerror ( rc ) );
+               goto err_xenbus_probe;
+       }
+
+       pci_set_drvdata ( pci, hvm );
+       return 0;
+
+       xenbus_remove ( &hvm->xen, &pci->dev );
+ err_xenbus_probe:
+       hvm_unmap_xenstore ( hvm );
+ err_map_xenstore:
+       hvm_unmap_grant ( hvm );
+ err_map_grant:
+       hvm_unmap_shared_info ( hvm );
+ err_map_shared_info:
+       hvm_unmap_hypercall ( hvm );
+ err_map_hypercall:
+ err_cpuid_base:
+       free ( hvm );
+ err_alloc:
+       return rc;
+}
+
+/**
+ * Remove PCI device
+ *
+ * @v pci              PCI device
+ */
+static void hvm_remove ( struct pci_device *pci ) {
+       struct hvm_device *hvm = pci_get_drvdata ( pci );
+
+       xenbus_remove ( &hvm->xen, &pci->dev );
+       hvm_unmap_xenstore ( hvm );
+       hvm_unmap_grant ( hvm );
+       hvm_unmap_shared_info ( hvm );
+       hvm_unmap_hypercall ( hvm );
+       free ( hvm );
+}
+
+/** PCI device IDs */
+static struct pci_device_id hvm_ids[] = {
+       PCI_ROM ( 0x5853, 0x0001, "hvm", "hvm", 0 ),
+       PCI_ROM ( 0x5853, 0x0002, "hvm2", "hvm2", 0 ),
+};
+
+/** PCI driver */
+struct pci_driver hvm_driver __pci_driver = {
+       .ids = hvm_ids,
+       .id_count = ( sizeof ( hvm_ids ) / sizeof ( hvm_ids[0] ) ),
+       .probe = hvm_probe,
+       .remove = hvm_remove,
+};
+
+/* Drag in objects via hvm_driver */
+REQUIRING_SYMBOL ( hvm_driver );
+
+/* Drag in netfront driver */
+//REQUIRE_OBJECT ( netfront );
diff --git a/IPXE/ipxe-3fe683e/src/arch/x86/interface/pcbios/hidemem.c b/IPXE/ipxe-3fe683e/src/arch/x86/interface/pcbios/hidemem.c
new file mode 100644 (file)
index 0000000..a8085af
--- /dev/null
@@ -0,0 +1,238 @@
+/* Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <assert.h>
+#include <realmode.h>
+#include <biosint.h>
+#include <basemem.h>
+#include <fakee820.h>
+#include <ipxe/init.h>
+#include <ipxe/io.h>
+#include <ipxe/hidemem.h>
+
+/** Set to true if you want to test a fake E820 map */
+#define FAKE_E820 0
+
+/** Alignment for hidden memory regions */
+#define ALIGN_HIDDEN 4096   /* 4kB page alignment should be enough */
+
+/**
+ * A hidden region of iPXE
+ *
+ * This represents a region that will be edited out of the system's
+ * memory map.
+ *
+ * This structure is accessed by assembly code, so must not be
+ * changed.
+ */
+struct hidden_region {
+       /** Physical start address */
+       uint64_t start;
+       /** Physical end address */
+       uint64_t end;
+};
+
+/** Hidden base memory */
+extern struct hidden_region __data16 ( hidemem_base );
+#define hidemem_base __use_data16 ( hidemem_base )
+
+/** Hidden umalloc memory */
+extern struct hidden_region __data16 ( hidemem_umalloc );
+#define hidemem_umalloc __use_data16 ( hidemem_umalloc )
+
+/** Hidden text memory */
+extern struct hidden_region __data16 ( hidemem_textdata );
+#define hidemem_textdata __use_data16 ( hidemem_textdata )
+
+/** Assembly routine in e820mangler.S */
+extern void int15();
+
+/** Vector for storing original INT 15 handler */
+extern struct segoff __text16 ( int15_vector );
+#define int15_vector __use_text16 ( int15_vector )
+
+/* The linker defines these symbols for us */
+extern char _textdata[];
+extern char _etextdata[];
+extern char _text16_memsz[];
+#define _text16_memsz ( ( size_t ) _text16_memsz )
+extern char _data16_memsz[];
+#define _data16_memsz ( ( size_t ) _data16_memsz )
+
+/**
+ * Hide region of memory from system memory map
+ *
+ * @v region           Hidden memory region
+ * @v start            Start of region
+ * @v end              End of region
+ */
+static void hide_region ( struct hidden_region *region,
+                         physaddr_t start, physaddr_t end ) {
+
+       /* Some operating systems get a nasty shock if a region of the
+        * E820 map seems to start on a non-page boundary.  Make life
+        * safer by rounding out our edited region.
+        */
+       region->start = ( start & ~( ALIGN_HIDDEN - 1 ) );
+       region->end = ( ( end + ALIGN_HIDDEN - 1 ) & ~( ALIGN_HIDDEN - 1 ) );
+
+       DBG ( "Hiding region [%llx,%llx)\n", region->start, region->end );
+}
+
+/**
+ * Hide used base memory
+ *
+ */
+void hide_basemem ( void ) {
+       /* Hide from the top of free base memory to 640kB.  Don't use
+        * hide_region(), because we don't want this rounded to the
+        * nearest page boundary.
+        */
+       hidemem_base.start = ( get_fbms() * 1024 );
+}
+
+/**
+ * Hide umalloc() region
+ *
+ */
+void hide_umalloc ( physaddr_t start, physaddr_t end ) {
+       assert ( end <= virt_to_phys ( _textdata ) );
+       hide_region ( &hidemem_umalloc, start, end );
+}
+
+/**
+ * Hide .text and .data
+ *
+ */
+void hide_textdata ( void ) {
+/* Deleted by longpanda */
+#if 0
+       hide_region ( &hidemem_textdata, virt_to_phys ( _textdata ),
+                     virt_to_phys ( _etextdata ) );
+#endif
+}
+
+/**
+ * Hide Etherboot
+ *
+ * Installs an INT 15 handler to edit Etherboot out of the memory map
+ * returned by the BIOS.
+ */
+static void hide_etherboot ( void ) {
+       struct memory_map memmap;
+       unsigned int rm_ds_top;
+       unsigned int rm_cs_top;
+       unsigned int fbms;
+
+       /* Dump memory map before mangling */
+       DBG ( "Hiding iPXE from system memory map\n" );
+       get_memmap ( &memmap );
+
+       /* Hook in fake E820 map, if we're testing one */
+       if ( FAKE_E820 ) {
+               DBG ( "Hooking in fake E820 map\n" );
+               fake_e820();
+               get_memmap ( &memmap );
+       }
+
+       /* Initialise the hidden regions */
+       hide_basemem();
+       hide_umalloc ( virt_to_phys ( _textdata ), virt_to_phys ( _textdata ) );
+       hide_textdata();
+
+       /* Some really moronic BIOSes bring up the PXE stack via the
+        * UNDI loader entry point and then don't bother to unload it
+        * before overwriting the code and data segments.  If this
+        * happens, we really don't want to leave INT 15 hooked,
+        * because that will cause any loaded OS to die horribly as
+        * soon as it attempts to fetch the system memory map.
+        *
+        * We use a heuristic to guess whether or not we are being
+        * loaded sensibly.
+        */
+       rm_cs_top = ( ( ( rm_cs << 4 ) + _text16_memsz + 1024 - 1 ) >> 10 );
+       rm_ds_top = ( ( ( rm_ds << 4 ) + _data16_memsz + 1024 - 1 ) >> 10 );
+       fbms = get_fbms();
+       if ( ( rm_cs_top < fbms ) && ( rm_ds_top < fbms ) ) {
+               DBG ( "Detected potentially unsafe UNDI load at CS=%04x "
+                     "DS=%04x FBMS=%dkB\n", rm_cs, rm_ds, fbms );
+               DBG ( "Disabling INT 15 memory hiding\n" );
+               return;
+       }
+
+       /* Hook INT 15 */
+       hook_bios_interrupt ( 0x15, ( intptr_t ) int15, &int15_vector );
+
+       /* Dump memory map after mangling */
+       DBG ( "Hidden iPXE from system memory map\n" );
+       get_memmap ( &memmap );
+}
+
+/**
+ * Unhide Etherboot
+ *
+ * Uninstalls the INT 15 handler installed by hide_etherboot(), if
+ * possible.
+ */
+static void unhide_etherboot ( int flags __unused ) {
+       struct memory_map memmap;
+       int rc;
+
+       /* If we have more than one hooked interrupt at this point, it
+        * means that some other vector is still hooked, in which case
+        * we can't safely unhook INT 15 because we need to keep our
+        * memory protected.  (We expect there to be at least one
+        * hooked interrupt, because INT 15 itself is still hooked).
+        */
+       if ( hooked_bios_interrupts > 1 ) {
+               DBG ( "Cannot unhide: %d interrupt vectors still hooked\n",
+                     hooked_bios_interrupts );
+               return;
+       }
+
+       /* Try to unhook INT 15 */
+       if ( ( rc = unhook_bios_interrupt ( 0x15, ( intptr_t ) int15,
+                                           &int15_vector ) ) != 0 ) {
+               DBG ( "Cannot unhook INT15: %s\n", strerror ( rc ) );
+               /* Leave it hooked; there's nothing else we can do,
+                * and it should be intrinsically safe (though
+                * wasteful of RAM).
+                */
+       }
+
+       /* Unhook fake E820 map, if used */
+       if ( FAKE_E820 )
+               unfake_e820();
+
+       /* Dump memory map after unhiding */
+       DBG ( "Unhidden iPXE from system memory map\n" );
+       get_memmap ( &memmap );
+}
+
+/** Hide Etherboot startup function */
+struct startup_fn hide_etherboot_startup_fn __startup_fn ( STARTUP_EARLY ) = {
+       .name = "hidemem",
+       .startup = hide_etherboot,
+       .shutdown = unhide_etherboot,
+};
diff --git a/IPXE/ipxe-3fe683e/src/arch/x86/interface/pcbios/int13.c b/IPXE/ipxe-3fe683e/src/arch/x86/interface/pcbios/int13.c
new file mode 100644 (file)
index 0000000..fe8c4b4
--- /dev/null
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <errno.h>
+#include <ipxe/sanboot.h>
+
+static int null_san_hook ( unsigned int drive __unused,
+                          struct uri **uris __unused,
+                          unsigned int count __unused,
+                          unsigned int flags __unused ) {
+       return -EOPNOTSUPP;
+}
+
+static void null_san_unhook ( unsigned int drive __unused ) {
+       /* Do nothing */
+}
+
+static int null_san_boot ( unsigned int drive __unused,
+                          const char *filename __unused ) {
+       return -EOPNOTSUPP;
+}
+
+static int null_san_describe ( void ) {
+       return -EOPNOTSUPP;
+}
+
+PROVIDE_SANBOOT ( pcbios, san_hook, null_san_hook );
+PROVIDE_SANBOOT ( pcbios, san_unhook, null_san_unhook );
+PROVIDE_SANBOOT ( pcbios, san_boot, null_san_boot );
+PROVIDE_SANBOOT ( pcbios, san_describe, null_san_describe );
+
diff --git a/IPXE/ipxe-3fe683e/src/arch/x86/interface/pcbios/ventoy_int13.c b/IPXE/ipxe-3fe683e/src/arch/x86/interface/pcbios/ventoy_int13.c
new file mode 100644 (file)
index 0000000..c71f9f6
--- /dev/null
@@ -0,0 +1,1527 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ * Copyright (C) 2020 longpanda <admin@ventoy.net>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stdio.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <limits.h>
+#include <byteswap.h>
+#include <errno.h>
+#include <assert.h>
+#include <ipxe/blockdev.h>
+#include <ipxe/io.h>
+#include <ipxe/acpi.h>
+#include <ipxe/sanboot.h>
+#include <ipxe/device.h>
+#include <ipxe/pci.h>
+#include <ipxe/timer.h>
+#include <ipxe/eltorito.h>
+#include <ipxe/umalloc.h>
+#include <ipxe/acpi.h>
+#include <ipxe/ibft.h>
+#include <realmode.h>
+#include <bios.h>
+#include <biosint.h>
+#include <bootsector.h>
+#include <int13.h>
+#include <ventoy.h>
+#include "ventoy_int13.h"
+
+static unsigned int g_drive_map1 = 0;
+static unsigned int g_drive_map2 = 0;
+
+/** @file
+ *
+ * INT 13 emulation
+ *
+ * This module provides a mechanism for exporting block devices via
+ * the BIOS INT 13 disk interrupt interface.  
+ *
+ */
+
+/** INT 13 SAN device private data */
+struct int13_data {
+       /** BIOS natural drive number (0x00-0xff)
+        *
+        * This is the drive number that would have been assigned by
+        * 'naturally' appending the drive to the end of the BIOS
+        * drive list.
+        *
+        * If the emulated drive replaces a preexisting drive, this is
+        * the drive number that the preexisting drive gets remapped
+        * to.
+        */
+       unsigned int natural_drive;
+
+       /** Number of cylinders
+        *
+        * The cylinder number field in an INT 13 call is ten bits
+        * wide, giving a maximum of 1024 cylinders.  Conventionally,
+        * when the 7.8GB limit of a CHS address is exceeded, it is
+        * the number of cylinders that is increased beyond the
+        * addressable limit.
+        */
+       unsigned int cylinders;
+       /** Number of heads
+        *
+        * The head number field in an INT 13 call is eight bits wide,
+        * giving a maximum of 256 heads.  However, apparently all
+        * versions of MS-DOS up to and including Win95 fail with 256
+        * heads, so the maximum encountered in practice is 255.
+        */
+       unsigned int heads;
+       /** Number of sectors per track
+        *
+        * The sector number field in an INT 13 call is six bits wide,
+        * giving a maximum of 63 sectors, since sector numbering
+        * (unlike head and cylinder numbering) starts at 1, not 0.
+        */
+       unsigned int sectors_per_track;
+
+       /** Address of El Torito boot catalog (if any) */
+       unsigned int boot_catalog;
+       /** Status of last operation */
+       int last_status;
+};
+
+/** Vector for chaining to other INT 13 handlers */
+static struct segoff __text16 ( int13_vector );
+#define int13_vector __use_text16 ( int13_vector )
+
+/** Assembly wrapper */
+extern void int13_wrapper ( void );
+
+/** Dummy floppy disk parameter table */
+static struct int13_fdd_parameters __data16 ( int13_fdd_params ) = {
+       /* 512 bytes per sector */
+       .bytes_per_sector = 0x02,
+       /* Highest sectors per track that we ever return */
+       .sectors_per_track = 48,
+};
+#define int13_fdd_params __use_data16 ( int13_fdd_params )
+
+/**
+ * Equipment word
+ *
+ * This is a cached copy of the BIOS Data Area equipment word at
+ * 40:10.
+ */
+static uint16_t equipment_word;
+
+/**
+ * Number of BIOS floppy disk drives
+ *
+ * This is derived from the equipment word.  It is held in .text16 to
+ * allow for easy access by the INT 13,08 wrapper.
+ */
+static uint8_t __text16 ( num_fdds );
+#define num_fdds __use_text16 ( num_fdds )
+
+/**
+ * Number of BIOS hard disk drives
+ *
+ * This is a cached copy of the BIOS Data Area number of hard disk
+ * drives at 40:75.  It is held in .text16 to allow for easy access by
+ * the INT 13,08 wrapper.
+ */
+static uint8_t __text16 ( num_drives );
+#define num_drives __use_text16 ( num_drives )
+static struct san_device *g_sandev;
+
+/**
+ * Calculate SAN device capacity (limited to 32 bits)
+ *
+ * @v sandev           SAN device
+ * @ret blocks         Number of blocks
+ */
+static inline uint32_t int13_capacity32 ( struct san_device *sandev ) {
+       uint64_t capacity = sandev_capacity ( sandev );
+       return ( ( capacity <= 0xffffffffUL ) ? capacity : 0xffffffff );
+}
+
+/**
+ * Test if SAN device is a floppy disk drive
+ *
+ * @v sandev           SAN device
+ * @ret is_fdd         SAN device is a floppy disk drive
+ */
+static inline int int13_is_fdd ( struct san_device *sandev ) {
+    (void)sandev;
+       return 0;
+}
+
+#if 0
+/**
+ * Guess INT 13 hard disk drive geometry
+ *
+ * @v sandev           SAN device
+ * @v scratch          Scratch area for single-sector reads
+ * @ret heads          Guessed number of heads
+ * @ret sectors                Guessed number of sectors per track
+ * @ret rc             Return status code
+ *
+ * Guesses the drive geometry by inspecting the partition table.
+ */
+static int int13_guess_geometry_hdd ( struct san_device *sandev, void *scratch,
+                                     unsigned int *heads,
+                                     unsigned int *sectors ) {
+       struct master_boot_record *mbr = scratch;
+       struct partition_table_entry *partition;
+       unsigned int i;
+       unsigned int start_cylinder;
+       unsigned int start_head;
+       unsigned int start_sector;
+       unsigned int end_head;
+       unsigned int end_sector;
+       int rc;
+
+       /* Read partition table */
+       if ( ( rc = sandev_read ( sandev, 0, 1, virt_to_user ( mbr ) ) ) != 0 ) {
+               DBGC ( sandev, "INT13 drive %02x could not read "
+                      "partition table to guess geometry: %s\n",
+                      sandev->drive, strerror ( rc ) );
+               return rc;
+       }
+       DBGC2 ( sandev, "INT13 drive %02x has MBR:\n", sandev->drive );
+       DBGC2_HDA ( sandev, 0, mbr, sizeof ( *mbr ) );
+       DBGC ( sandev, "INT13 drive %02x has signature %08x\n",
+              sandev->drive, mbr->signature );
+
+       /* Scan through partition table and modify guesses for
+        * heads and sectors_per_track if we find any used
+        * partitions.
+        */
+       *heads = 0;
+       *sectors = 0;
+       for ( i = 0 ; i < 4 ; i++ ) {
+
+               /* Skip empty partitions */
+               partition = &mbr->partitions[i];
+               if ( ! partition->type )
+                       continue;
+
+               /* If partition starts on cylinder 0 then we can
+                * unambiguously determine the number of sectors.
+                */
+               start_cylinder = PART_CYLINDER ( partition->chs_start );
+               start_head = PART_HEAD ( partition->chs_start );
+               start_sector = PART_SECTOR ( partition->chs_start );
+               if ( ( start_cylinder == 0 ) && ( start_head != 0 ) ) {
+                       *sectors = ( ( partition->start + 1 - start_sector ) /
+                                    start_head );
+                       DBGC ( sandev, "INT13 drive %02x guessing C/H/S "
+                              "xx/xx/%d based on partition %d\n",
+                              sandev->drive, *sectors, ( i + 1 ) );
+               }
+
+               /* If partition ends on a higher head or sector number
+                * than our current guess, then increase the guess.
+                */
+               end_head = PART_HEAD ( partition->chs_end );
+               end_sector = PART_SECTOR ( partition->chs_end );
+               if ( ( end_head + 1 ) > *heads ) {
+                       *heads = ( end_head + 1 );
+                       DBGC ( sandev, "INT13 drive %02x guessing C/H/S "
+                              "xx/%d/xx based on partition %d\n",
+                              sandev->drive, *heads, ( i + 1 ) );
+               }
+               if ( end_sector > *sectors ) {
+                       *sectors = end_sector;
+                       DBGC ( sandev, "INT13 drive %02x guessing C/H/S "
+                              "xx/xx/%d based on partition %d\n",
+                              sandev->drive, *sectors, ( i + 1 ) );
+               }
+       }
+
+       /* Default guess is xx/255/63 */
+       if ( ! *heads )
+               *heads = 255;
+       if ( ! *sectors )
+               *sectors = 63;
+
+       return 0;
+}
+
+/** Recognised floppy disk geometries */
+static const struct int13_fdd_geometry int13_fdd_geometries[] = {
+       INT13_FDD_GEOMETRY ( 40, 1, 8 ),
+       INT13_FDD_GEOMETRY ( 40, 1, 9 ),
+       INT13_FDD_GEOMETRY ( 40, 2, 8 ),
+       INT13_FDD_GEOMETRY ( 40, 1, 9 ),
+       INT13_FDD_GEOMETRY ( 80, 2, 8 ),
+       INT13_FDD_GEOMETRY ( 80, 2, 9 ),
+       INT13_FDD_GEOMETRY ( 80, 2, 15 ),
+       INT13_FDD_GEOMETRY ( 80, 2, 18 ),
+       INT13_FDD_GEOMETRY ( 80, 2, 20 ),
+       INT13_FDD_GEOMETRY ( 80, 2, 21 ),
+       INT13_FDD_GEOMETRY ( 82, 2, 21 ),
+       INT13_FDD_GEOMETRY ( 83, 2, 21 ),
+       INT13_FDD_GEOMETRY ( 80, 2, 22 ),
+       INT13_FDD_GEOMETRY ( 80, 2, 23 ),
+       INT13_FDD_GEOMETRY ( 80, 2, 24 ),
+       INT13_FDD_GEOMETRY ( 80, 2, 36 ),
+       INT13_FDD_GEOMETRY ( 80, 2, 39 ),
+       INT13_FDD_GEOMETRY ( 80, 2, 40 ),
+       INT13_FDD_GEOMETRY ( 80, 2, 44 ),
+       INT13_FDD_GEOMETRY ( 80, 2, 48 ),
+};
+
+/**
+ * Guess INT 13 floppy disk drive geometry
+ *
+ * @v sandev           SAN device
+ * @ret heads          Guessed number of heads
+ * @ret sectors                Guessed number of sectors per track
+ * @ret rc             Return status code
+ *
+ * Guesses the drive geometry by inspecting the disk size.
+ */
+static int int13_guess_geometry_fdd ( struct san_device *sandev,
+                                     unsigned int *heads,
+                                     unsigned int *sectors ) {
+       unsigned int blocks = sandev_capacity ( sandev );
+       const struct int13_fdd_geometry *geometry;
+       unsigned int cylinders;
+       unsigned int i;
+
+       /* Look for a match against a known geometry */
+       for ( i = 0 ; i < ( sizeof ( int13_fdd_geometries ) /
+                           sizeof ( int13_fdd_geometries[0] ) ) ; i++ ) {
+               geometry = &int13_fdd_geometries[i];
+               cylinders = INT13_FDD_CYLINDERS ( geometry );
+               *heads = INT13_FDD_HEADS ( geometry );
+               *sectors = INT13_FDD_SECTORS ( geometry );
+               if ( ( cylinders * (*heads) * (*sectors) ) == blocks ) {
+                       DBGC ( sandev, "INT13 drive %02x guessing C/H/S "
+                              "%d/%d/%d based on size %dK\n", sandev->drive,
+                              cylinders, *heads, *sectors, ( blocks / 2 ) );
+                       return 0;
+               }
+       }
+
+       /* Otherwise, assume a partial disk image in the most common
+        * format (1440K, 80/2/18).
+        */
+       *heads = 2;
+       *sectors = 18;
+       DBGC ( sandev, "INT13 drive %02x guessing C/H/S xx/%d/%d based on size "
+              "%dK\n", sandev->drive, *heads, *sectors, ( blocks / 2 ) );
+       return 0;
+}
+
+/**
+ * Guess INT 13 drive geometry
+ *
+ * @v sandev           SAN device
+ * @v scratch          Scratch area for single-sector reads
+ * @ret rc             Return status code
+ */
+static int int13_guess_geometry ( struct san_device *sandev, void *scratch ) {
+       struct int13_data *int13 = sandev->priv;
+       unsigned int guessed_heads;
+       unsigned int guessed_sectors;
+       unsigned int blocks;
+       unsigned int blocks_per_cyl;
+       int rc;
+
+       /* Guess geometry according to drive type */
+       if ( int13_is_fdd ( sandev ) ) {
+               if ( ( rc = int13_guess_geometry_fdd ( sandev, &guessed_heads,
+                                                      &guessed_sectors )) != 0)
+                       return rc;
+       } else {
+               if ( ( rc = int13_guess_geometry_hdd ( sandev, scratch,
+                                                      &guessed_heads,
+                                                      &guessed_sectors )) != 0)
+                       return rc;
+       }
+
+       /* Apply guesses if no geometry already specified */
+       if ( ! int13->heads )
+               int13->heads = guessed_heads;
+       if ( ! int13->sectors_per_track )
+               int13->sectors_per_track = guessed_sectors;
+       if ( ! int13->cylinders ) {
+               /* Avoid attempting a 64-bit divide on a 32-bit system */
+               blocks = int13_capacity32 ( sandev );
+               blocks_per_cyl = ( int13->heads * int13->sectors_per_track );
+               assert ( blocks_per_cyl != 0 );
+               int13->cylinders = ( blocks / blocks_per_cyl );
+               if ( int13->cylinders > 1024 )
+                       int13->cylinders = 1024;
+       }
+
+       return 0;
+}
+#endif /* #if 0 */
+
+/**
+ * Update BIOS drive count
+ */
+void int13_sync_num_drives ( void ) {
+       struct san_device *sandev;
+       struct int13_data *int13;
+       uint8_t *counter;
+       uint8_t max_drive;
+       uint8_t required;
+
+       /* Get current drive counts */
+       get_real ( equipment_word, BDA_SEG, BDA_EQUIPMENT_WORD );
+       get_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
+       num_fdds = ( ( equipment_word & 0x0001 ) ?
+                    ( ( ( equipment_word >> 6 ) & 0x3 ) + 1 ) : 0 );
+
+       /* Ensure count is large enough to cover all of our SAN devices */
+       for_each_sandev ( sandev ) {
+               int13 = sandev->priv;
+               counter = ( int13_is_fdd ( sandev ) ? &num_fdds : &num_drives );
+               max_drive = sandev->drive;
+               if ( max_drive < int13->natural_drive )
+                       max_drive = int13->natural_drive;
+               required = ( ( max_drive & 0x7f ) + 1 );
+               if ( *counter < required ) {
+                       *counter = required;
+                       DBGC ( sandev, "INT13 drive %02x added to drive count: "
+                              "%d HDDs, %d FDDs\n",
+                              sandev->drive, num_drives, num_fdds );
+               }
+       }
+
+       /* Update current drive count */
+       equipment_word &= ~( ( 0x3 << 6 ) | 0x0001 );
+       if ( num_fdds ) {
+               equipment_word |= ( 0x0001 |
+                                   ( ( ( num_fdds - 1 ) & 0x3 ) << 6 ) );
+       }
+       put_real ( equipment_word, BDA_SEG, BDA_EQUIPMENT_WORD );
+       put_real ( num_drives, BDA_SEG, BDA_NUM_DRIVES );
+}
+
+/**
+ * Check number of drives
+ */
+void int13_check_num_drives ( void ) {
+       uint16_t check_equipment_word;
+       uint8_t check_num_drives;
+
+       get_real ( check_equipment_word, BDA_SEG, BDA_EQUIPMENT_WORD );
+       get_real ( check_num_drives, BDA_SEG, BDA_NUM_DRIVES );
+       if ( ( check_equipment_word != equipment_word ) ||
+            ( check_num_drives != num_drives ) ) {
+               int13_sync_num_drives();
+       }
+}
+
+/**
+ * INT 13, 00 - Reset disk system
+ *
+ * @v sandev           SAN device
+ * @ret status         Status code
+ */
+static int int13_reset ( struct san_device *sandev,
+                        struct i386_all_regs *ix86 __unused ) {
+       int rc;
+
+       DBGC2 ( sandev, "Reset drive\n" );
+
+       /* Reset SAN device */
+       if ( ( rc = sandev_reset ( sandev ) ) != 0 )
+               return -INT13_STATUS_RESET_FAILED;
+
+       return 0;
+}
+
+/**
+ * INT 13, 01 - Get status of last operation
+ *
+ * @v sandev           SAN device
+ * @ret status         Status code
+ */
+static int int13_get_last_status ( struct san_device *sandev,
+                                  struct i386_all_regs *ix86 __unused ) {
+       struct int13_data *int13 = sandev->priv;
+
+       DBGC2 ( sandev, "Get status of last operation\n" );
+       return int13->last_status;
+}
+
+/**
+ * Read / write sectors
+ *
+ * @v sandev           SAN device
+ * @v al               Number of sectors to read or write (must be nonzero)
+ * @v ch               Low bits of cylinder number
+ * @v cl (bits 7:6)    High bits of cylinder number
+ * @v cl (bits 5:0)    Sector number
+ * @v dh               Head number
+ * @v es:bx            Data buffer
+ * @v sandev_rw                SAN device read/write method
+ * @ret status         Status code
+ * @ret al             Number of sectors read or written
+ */
+static int int13_rw_sectors ( struct san_device *sandev,
+                             struct i386_all_regs *ix86,
+                             int ( * sandev_rw ) ( struct san_device *sandev,
+                                                   uint64_t lba,
+                                                   unsigned int count,
+                                                   userptr_t buffer ) ) {
+       struct int13_data *int13 = sandev->priv;
+       unsigned int cylinder, head, sector;
+       unsigned long lba;
+       unsigned int count;
+       userptr_t buffer;
+       int rc;
+
+       /* Validate blocksize */
+       if ( sandev_blksize ( sandev ) != INT13_BLKSIZE ) {
+               DBGC ( sandev, "\nINT 13 drive %02x invalid blocksize (%zd) "
+                      "for non-extended read/write\n",
+                      sandev->drive, sandev_blksize ( sandev ) );
+               return -INT13_STATUS_INVALID;
+       }
+
+       /* Calculate parameters */
+       cylinder = ( ( ( ix86->regs.cl & 0xc0 ) << 2 ) | ix86->regs.ch );
+       head = ix86->regs.dh;
+       sector = ( ix86->regs.cl & 0x3f );
+       if ( ( cylinder >= int13->cylinders ) ||
+            ( head >= int13->heads ) ||
+            ( sector < 1 ) || ( sector > int13->sectors_per_track ) ) {
+               DBGC ( sandev, "C/H/S %d/%d/%d out of range for geometry "
+                      "%d/%d/%d\n", cylinder, head, sector, int13->cylinders,
+                      int13->heads, int13->sectors_per_track );
+               return -INT13_STATUS_INVALID;
+       }
+       lba = ( ( ( ( cylinder * int13->heads ) + head )
+                 * int13->sectors_per_track ) + sector - 1 );
+       count = ix86->regs.al;
+       buffer = real_to_user ( ix86->segs.es, ix86->regs.bx );
+
+       DBGC2 ( sandev, "C/H/S %d/%d/%d = LBA %08lx <-> %04x:%04x (count %d)\n",
+               cylinder, head, sector, lba, ix86->segs.es, ix86->regs.bx,
+               count );
+
+       /* Read from / write to block device */
+       if ( ( rc = sandev_rw ( sandev, lba, count, buffer ) ) != 0 ){
+               DBGC ( sandev, "INT13 drive %02x I/O failed: %s\n",
+                      sandev->drive, strerror ( rc ) );
+               return -INT13_STATUS_READ_ERROR;
+       }
+
+       return 0;
+}
+
+/**
+ * INT 13, 02 - Read sectors
+ *
+ * @v sandev           SAN device
+ * @v al               Number of sectors to read (must be nonzero)
+ * @v ch               Low bits of cylinder number
+ * @v cl (bits 7:6)    High bits of cylinder number
+ * @v cl (bits 5:0)    Sector number
+ * @v dh               Head number
+ * @v es:bx            Data buffer
+ * @ret status         Status code
+ * @ret al             Number of sectors read
+ */
+static int int13_read_sectors ( struct san_device *sandev,
+                               struct i386_all_regs *ix86 ) {
+
+       DBGC2 ( sandev, "Read: " );
+       return int13_rw_sectors ( sandev, ix86, sandev_read );
+}
+
+/**
+ * INT 13, 03 - Write sectors
+ *
+ * @v sandev           SAN device
+ * @v al               Number of sectors to write (must be nonzero)
+ * @v ch               Low bits of cylinder number
+ * @v cl (bits 7:6)    High bits of cylinder number
+ * @v cl (bits 5:0)    Sector number
+ * @v dh               Head number
+ * @v es:bx            Data buffer
+ * @ret status         Status code
+ * @ret al             Number of sectors written
+ */
+static int int13_write_sectors ( struct san_device *sandev,
+                                struct i386_all_regs *ix86 ) {
+
+       DBGC2 ( sandev, "Write: " );
+       return int13_rw_sectors ( sandev, ix86, sandev_write );
+}
+
+/**
+ * INT 13, 08 - Get drive parameters
+ *
+ * @v sandev           SAN device
+ * @ret status         Status code
+ * @ret ch             Low bits of maximum cylinder number
+ * @ret cl (bits 7:6)  High bits of maximum cylinder number
+ * @ret cl (bits 5:0)  Maximum sector number
+ * @ret dh             Maximum head number
+ * @ret dl             Number of drives
+ */
+static int int13_get_parameters ( struct san_device *sandev,
+                                 struct i386_all_regs *ix86 ) {
+       struct int13_data *int13 = sandev->priv;
+       unsigned int max_cylinder = int13->cylinders - 1;
+       unsigned int max_head = int13->heads - 1;
+       unsigned int max_sector = int13->sectors_per_track; /* sic */
+
+       DBGC2 ( sandev, "Get drive parameters\n" );
+
+       /* Validate blocksize */
+       if ( sandev_blksize ( sandev ) != INT13_BLKSIZE ) {
+               DBGC ( sandev, "\nINT 13 drive %02x invalid blocksize (%zd) "
+                      "for non-extended parameters\n",
+                      sandev->drive, sandev_blksize ( sandev ) );
+               return -INT13_STATUS_INVALID;
+       }
+
+       /* Common parameters */
+       ix86->regs.ch = ( max_cylinder & 0xff );
+       ix86->regs.cl = ( ( ( max_cylinder >> 8 ) << 6 ) | max_sector );
+       ix86->regs.dh = max_head;
+       ix86->regs.dl = ( int13_is_fdd ( sandev ) ? num_fdds : num_drives );
+
+       /* Floppy-specific parameters */
+       if ( int13_is_fdd ( sandev ) ) {
+               ix86->regs.bl = INT13_FDD_TYPE_1M44;
+               ix86->segs.es = rm_ds;
+               ix86->regs.di = __from_data16 ( &int13_fdd_params );
+       }
+
+       return 0;
+}
+
+/**
+ * INT 13, 15 - Get disk type
+ *
+ * @v sandev           SAN device
+ * @ret ah             Type code
+ * @ret cx:dx          Sector count
+ * @ret status         Status code / disk type
+ */
+static int int13_get_disk_type ( struct san_device *sandev,
+                                struct i386_all_regs *ix86 ) {
+       uint32_t blocks;
+
+       DBGC2 ( sandev, "Get disk type\n" );
+
+       if ( int13_is_fdd ( sandev ) ) {
+               return INT13_DISK_TYPE_FDD;
+       } else {
+               blocks = int13_capacity32 ( sandev );
+               ix86->regs.cx = ( blocks >> 16 );
+               ix86->regs.dx = ( blocks & 0xffff );
+               return INT13_DISK_TYPE_HDD;
+       }
+}
+
+/**
+ * INT 13, 41 - Extensions installation check
+ *
+ * @v sandev           SAN device
+ * @v bx               0x55aa
+ * @ret bx             0xaa55
+ * @ret cx             Extensions API support bitmap
+ * @ret status         Status code / API version
+ */
+static int int13_extension_check ( struct san_device *sandev __unused,
+                                  struct i386_all_regs *ix86 ) {
+
+       if ( ix86->regs.bx == 0x55aa ) {
+               DBGC2 ( sandev, "INT13 extensions installation check\n" );
+               ix86->regs.bx = 0xaa55;
+               ix86->regs.cx = ( INT13_EXTENSION_LINEAR |
+                                 INT13_EXTENSION_EDD |
+                                 INT13_EXTENSION_64BIT );
+               return INT13_EXTENSION_VER_3_0;
+       } else {
+               return -INT13_STATUS_INVALID;
+       }
+}
+
+/**
+ * Extended read / write
+ *
+ * @v sandev           SAN device
+ * @v ds:si            Disk address packet
+ * @v sandev_rw                SAN device read/write method
+ * @ret status         Status code
+ */
+static int int13_extended_rw ( struct san_device *sandev,
+                              struct i386_all_regs *ix86,
+                              int ( * sandev_rw ) ( struct san_device *sandev,
+                                                    uint64_t lba,
+                                                    unsigned int count,
+                                                    userptr_t buffer ) ) {
+       struct int13_disk_address addr;
+       uint8_t bufsize;
+       uint64_t lba;
+       unsigned long count;
+       userptr_t buffer;
+       int rc;
+
+       /* Extended reads are not allowed on floppy drives.
+        * ELTORITO.SYS seems to assume that we are really a CD-ROM if
+        * we support extended reads for a floppy drive.
+        */
+       if ( int13_is_fdd ( sandev ) )
+               return -INT13_STATUS_INVALID;
+
+       /* Get buffer size */
+       get_real ( bufsize, ix86->segs.ds,
+                  ( ix86->regs.si + offsetof ( typeof ( addr ), bufsize ) ) );
+       if ( bufsize < offsetof ( typeof ( addr ), buffer_phys ) ) {
+               DBGC2 ( sandev, "<invalid buffer size %#02x\n>\n", bufsize );
+               return -INT13_STATUS_INVALID;
+       }
+
+       /* Read parameters from disk address structure */
+       memset ( &addr, 0, sizeof ( addr ) );
+       copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si, bufsize );
+       lba = addr.lba;
+       DBGC2 ( sandev, "LBA %08llx <-> ", ( ( unsigned long long ) lba ) );
+       if ( ( addr.count == 0xff ) ||
+            ( ( addr.buffer.segment == 0xffff ) &&
+              ( addr.buffer.offset == 0xffff ) ) ) {
+               buffer = phys_to_user ( addr.buffer_phys );
+               DBGC2 ( sandev, "%08llx",
+                       ( ( unsigned long long ) addr.buffer_phys ) );
+       } else {
+               buffer = real_to_user ( addr.buffer.segment,
+                                       addr.buffer.offset );
+               DBGC2 ( sandev, "%04x:%04x", addr.buffer.segment,
+                       addr.buffer.offset );
+       }
+       if ( addr.count <= 0x7f ) {
+               count = addr.count;
+       } else if ( addr.count == 0xff ) {
+               count = addr.long_count;
+       } else {
+               DBGC2 ( sandev, " <invalid count %#02x>\n", addr.count );
+               return -INT13_STATUS_INVALID;
+       }
+       DBGC2 ( sandev, " (count %ld)\n", count );
+
+       /* Read from / write to block device */
+       if ( ( rc = sandev_rw ( sandev, lba, count, buffer ) ) != 0 ) {
+               DBGC ( sandev, "INT13 drive %02x extended I/O failed: %s\n",
+                      sandev->drive, strerror ( rc ) );
+               /* Record that no blocks were transferred successfully */
+               addr.count = 0;
+               put_real ( addr.count, ix86->segs.ds,
+                          ( ix86->regs.si +
+                            offsetof ( typeof ( addr ), count ) ) );
+               return -INT13_STATUS_READ_ERROR;
+       }
+
+    copy_to_real (ix86->segs.ds, ix86->regs.si, &addr, bufsize );
+
+       return 0;
+}
+
+/**
+ * INT 13, 42 - Extended read
+ *
+ * @v sandev           SAN device
+ * @v ds:si            Disk address packet
+ * @ret status         Status code
+ */
+static int int13_extended_read ( struct san_device *sandev,
+                                struct i386_all_regs *ix86 ) {
+
+       DBGC2 ( sandev, "Extended read: " );
+       return int13_extended_rw ( sandev, ix86, sandev_read );
+}
+
+/**
+ * INT 13, 43 - Extended write
+ *
+ * @v sandev           SAN device
+ * @v ds:si            Disk address packet
+ * @ret status         Status code
+ */
+static int int13_extended_write ( struct san_device *sandev,
+                                 struct i386_all_regs *ix86 ) {
+
+       DBGC2 ( sandev, "Extended write: " );
+       return int13_extended_rw ( sandev, ix86, sandev_write );
+}
+
+/**
+ * INT 13, 44 - Verify sectors
+ *
+ * @v sandev           SAN device
+ * @v ds:si            Disk address packet
+ * @ret status         Status code
+ */
+static int int13_extended_verify ( struct san_device *sandev,
+                                  struct i386_all_regs *ix86 ) {
+       struct int13_disk_address addr;
+       uint64_t lba;
+       unsigned long count;
+
+       /* Read parameters from disk address structure */
+       if ( DBG_EXTRA ) {
+               copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si,
+                                sizeof ( addr ));
+               lba = addr.lba;
+               count = addr.count;
+               DBGC2 ( sandev, "Verify: LBA %08llx (count %ld)\n",
+                       ( ( unsigned long long ) lba ), count );
+       }
+
+       /* We have no mechanism for verifying sectors */
+       return -INT13_STATUS_INVALID;
+}
+
+/**
+ * INT 13, 44 - Extended seek
+ *
+ * @v sandev           SAN device
+ * @v ds:si            Disk address packet
+ * @ret status         Status code
+ */
+static int int13_extended_seek ( struct san_device *sandev,
+                                struct i386_all_regs *ix86 ) {
+       struct int13_disk_address addr;
+       uint64_t lba;
+       unsigned long count;
+
+       /* Read parameters from disk address structure */
+       if ( DBG_EXTRA ) {
+               copy_from_real ( &addr, ix86->segs.ds, ix86->regs.si,
+                                sizeof ( addr ));
+               lba = addr.lba;
+               count = addr.count;
+               DBGC2 ( sandev, "Seek: LBA %08llx (count %ld)\n",
+                       ( ( unsigned long long ) lba ), count );
+       }
+
+       /* Ignore and return success */
+       return 0;
+}
+
+/**
+ * Build device path information
+ *
+ * @v sandev           SAN device
+ * @v dpi              Device path information
+ * @ret rc             Return status code
+ */
+static int int13_device_path_info ( struct san_device *sandev,
+                                   struct edd_device_path_information *dpi ) {
+       struct san_path *sanpath;
+       struct device *device;
+       struct device_description *desc;
+       unsigned int i;
+       uint8_t sum = 0;
+       int rc;
+    return -ECANCELED;
+       /* Reopen block device if necessary */
+       if ( sandev_needs_reopen ( sandev ) &&
+            ( ( rc = sandev_reopen ( sandev ) ) != 0 ) )
+               return rc;
+       sanpath = sandev->active;
+       assert ( sanpath != NULL );
+
+       /* Get underlying hardware device */
+       device = identify_device ( &sanpath->block );
+       if ( ! device ) {
+               DBGC ( sandev, "INT13 drive %02x cannot identify hardware "
+                      "device\n", sandev->drive );
+               return -ENODEV;
+       }
+
+       /* Fill in bus type and interface path */
+       desc = &device->desc;
+       switch ( desc->bus_type ) {
+       case BUS_TYPE_PCI:
+               dpi->host_bus_type.type = EDD_BUS_TYPE_PCI;
+               dpi->interface_path.pci.bus = PCI_BUS ( desc->location );
+               dpi->interface_path.pci.slot = PCI_SLOT ( desc->location );
+               dpi->interface_path.pci.function = PCI_FUNC ( desc->location );
+               dpi->interface_path.pci.channel = 0xff; /* unused */
+               break;
+       default:
+               DBGC ( sandev, "INT13 drive %02x unrecognised bus type %d\n",
+                      sandev->drive, desc->bus_type );
+               return -ENOTSUP;
+       }
+
+       /* Get EDD block device description */
+       if ( ( rc = edd_describe ( &sanpath->block, &dpi->interface_type,
+                                  &dpi->device_path ) ) != 0 ) {
+               DBGC ( sandev, "INT13 drive %02x cannot identify block device: "
+                      "%s\n", sandev->drive, strerror ( rc ) );
+               return rc;
+       }
+
+       /* Fill in common fields and fix checksum */
+       dpi->key = EDD_DEVICE_PATH_INFO_KEY;
+       dpi->len = sizeof ( *dpi );
+       for ( i = 0 ; i < sizeof ( *dpi ) ; i++ )
+               sum += *( ( ( uint8_t * ) dpi ) + i );
+       dpi->checksum -= sum;
+
+       return 0;
+}
+
+/**
+ * INT 13, 48 - Get extended parameters
+ *
+ * @v sandev           SAN device
+ * @v ds:si            Drive parameter table
+ * @ret status         Status code
+ */
+static int int13_get_extended_parameters ( struct san_device *sandev,
+                                          struct i386_all_regs *ix86 ) {
+       struct int13_data *int13 = sandev->priv;
+       struct int13_disk_parameters params;
+       struct segoff address;
+       size_t len = sizeof ( params );
+       uint16_t bufsize;
+       int rc;
+
+       /* Get buffer size */
+       get_real ( bufsize, ix86->segs.ds,
+                  ( ix86->regs.si + offsetof ( typeof ( params ), bufsize )));
+
+       DBGC2 ( sandev, "Get extended drive parameters to %04x:%04x+%02x\n",
+               ix86->segs.ds, ix86->regs.si, bufsize );
+
+       /* Build drive parameters */
+       memset ( &params, 0, sizeof ( params ) );
+       params.flags = INT13_FL_DMA_TRANSPARENT;
+       if ( ( int13->cylinders < 1024 ) &&
+            ( sandev_capacity ( sandev ) <= INT13_MAX_CHS_SECTORS ) ) {
+               params.flags |= INT13_FL_CHS_VALID;
+       }
+       params.cylinders = int13->cylinders;
+       params.heads = int13->heads;
+       params.sectors_per_track = int13->sectors_per_track;
+       params.sectors = sandev_capacity ( sandev );
+       params.sector_size = sandev_blksize ( sandev );
+       memset ( &params.dpte, 0xff, sizeof ( params.dpte ) );
+       if ( ( rc = int13_device_path_info ( sandev, &params.dpi ) ) != 0 ) {
+               DBGC ( sandev, "INT13 drive %02x could not provide device "
+                      "path information: %s\n",
+                      sandev->drive, strerror ( rc ) );
+               len = offsetof ( typeof ( params ), dpi );
+       }
+
+       /* Calculate returned "buffer size" (which will be less than
+        * the length actually copied if device path information is
+        * present).
+        */
+       if ( bufsize < offsetof ( typeof ( params ), dpte ) )
+               return -INT13_STATUS_INVALID;
+       if ( bufsize < offsetof ( typeof ( params ), dpi ) ) {
+               params.bufsize = offsetof ( typeof ( params ), dpte );
+       } else {
+               params.bufsize = offsetof ( typeof ( params ), dpi );
+       }
+
+       DBGC ( sandev, "INT 13 drive %02x described using extended "
+              "parameters:\n", sandev->drive );
+       address.segment = ix86->segs.ds;
+       address.offset = ix86->regs.si;
+       DBGC_HDA ( sandev, address, &params, len );
+
+       /* Return drive parameters */
+       if ( len > bufsize )
+               len = bufsize;
+       copy_to_real ( ix86->segs.ds, ix86->regs.si, &params, len );
+
+       return 0;
+}
+
+/**
+ * INT 13, 4b - Get status or terminate CD-ROM emulation
+ *
+ * @v sandev           SAN device
+ * @v ds:si            Specification packet
+ * @ret status         Status code
+ */
+static int int13_cdrom_status_terminate ( struct san_device *sandev,
+                                         struct i386_all_regs *ix86 ) {
+       struct int13_cdrom_specification specification;
+
+       DBGC2 ( sandev, "Get CD-ROM emulation status to %04x:%04x%s\n",
+               ix86->segs.ds, ix86->regs.si,
+               ( ix86->regs.al ? "" : " and terminate" ) );
+
+       /* Fail if we are not a CD-ROM */
+       if ( ! sandev->is_cdrom ) {
+               DBGC ( sandev, "INT13 drive %02x is not a CD-ROM\n",
+                      sandev->drive );
+               return -INT13_STATUS_INVALID;
+       }
+
+       /* Build specification packet */
+       memset ( &specification, 0, sizeof ( specification ) );
+       specification.size = sizeof ( specification );
+       specification.drive = sandev->drive;
+
+       /* Return specification packet */
+       copy_to_real ( ix86->segs.ds, ix86->regs.si, &specification,
+                      sizeof ( specification ) );
+
+       return 0;
+}
+
+
+/**
+ * INT 13, 4d - Read CD-ROM boot catalog
+ *
+ * @v sandev           SAN device
+ * @v ds:si            Command packet
+ * @ret status         Status code
+ */
+static int int13_cdrom_read_boot_catalog ( struct san_device *sandev,
+                                          struct i386_all_regs *ix86 ) {
+       struct int13_data *int13 = sandev->priv;
+       struct int13_cdrom_boot_catalog_command command;
+       unsigned int start;
+       int rc;
+
+       /* Read parameters from command packet */
+       copy_from_real ( &command, ix86->segs.ds, ix86->regs.si,
+                        sizeof ( command ) );
+       DBGC2 ( sandev, "Read CD-ROM boot catalog to %08x\n", command.buffer );
+
+       /* Fail if we have no boot catalog */
+       if ( ! int13->boot_catalog ) {
+               DBGC ( sandev, "INT13 drive %02x has no boot catalog\n",
+                      sandev->drive );
+               return -INT13_STATUS_INVALID;
+       }
+       start = ( int13->boot_catalog + command.start );
+
+       /* Read from boot catalog */
+       if ( ( rc = sandev_read ( sandev, start, command.count,
+                                 phys_to_user ( command.buffer ) ) ) != 0 ) {
+               DBGC ( sandev, "INT13 drive %02x could not read boot catalog: "
+                      "%s\n", sandev->drive, strerror ( rc ) );
+               return -INT13_STATUS_READ_ERROR;
+       }
+
+       return 0;
+}
+
+
+
+/**
+ * INT 13 handler
+ *
+ */
+static __asmcall void int13 ( struct i386_all_regs *ix86 ) {
+       int command = ix86->regs.ah;
+       unsigned int bios_drive = ix86->regs.dl;
+       struct san_device *sandev;
+       struct int13_data *int13;
+       int status;
+
+       /* We simulate a cdrom, so no need to sync hd drive number */
+       //int13_check_num_drives();
+
+    if (bios_drive == VENTOY_BIOS_FAKE_DRIVE)
+    {
+        ix86->regs.dl = g_sandev->exdrive;
+        return;
+    }
+
+    // drive swap
+    if (g_drive_map1 >= 0x80 && g_drive_map2 >= 0x80)
+    {
+        if (bios_drive == g_drive_map1)
+        {
+            ix86->regs.dl = g_drive_map2;
+            return;
+        }
+        else if (bios_drive == g_drive_map2)
+        {
+            ix86->regs.dl = g_drive_map1;
+            return;
+        }
+    }
+
+       for_each_sandev ( sandev ) {
+
+               int13 = sandev->priv;
+               if ( bios_drive != sandev->drive ) {
+                       /* Remap any accesses to this drive's natural number */
+                       if ( bios_drive == int13->natural_drive ) {
+                               DBGC2 ( sandev, "INT13,%02x (%02x) remapped to "
+                                       "(%02x)\n", ix86->regs.ah,
+                                       bios_drive, sandev->drive );
+                               ix86->regs.dl = sandev->drive;
+                               return;
+                       } else if ( ( ( bios_drive & 0x7f ) == 0x7f ) &&
+                                   ( command == INT13_CDROM_STATUS_TERMINATE )
+                                   && sandev->is_cdrom ) {
+                    
+                               /* Catch non-drive-specific CD-ROM calls */
+                       } else {
+                               return;
+                       }
+               }
+
+        sandev->int13_command = command;
+        sandev->x86_regptr = ix86;        
+
+               DBGC2 ( sandev, "INT13,%02x (%02x): ",
+                       ix86->regs.ah, bios_drive );
+
+               switch ( command ) {
+               case INT13_RESET:
+                       status = int13_reset ( sandev, ix86 );
+                       break;
+               case INT13_GET_LAST_STATUS:
+                       status = int13_get_last_status ( sandev, ix86 );
+                       break;
+               case INT13_READ_SECTORS:
+                       status = int13_read_sectors ( sandev, ix86 );
+                       break;
+               case INT13_WRITE_SECTORS:
+                       status = int13_write_sectors ( sandev, ix86 );
+                       break;
+               case INT13_GET_PARAMETERS:
+                       status = int13_get_parameters ( sandev, ix86 );
+                       break;
+               case INT13_GET_DISK_TYPE:
+                       status = int13_get_disk_type ( sandev, ix86 );
+                       break;
+               case INT13_EXTENSION_CHECK:
+                       status = int13_extension_check ( sandev, ix86 );
+                       break;
+               case INT13_EXTENDED_READ:
+                       status = int13_extended_read ( sandev, ix86 );
+                       break;
+               case INT13_EXTENDED_WRITE:
+                       status = int13_extended_write ( sandev, ix86 );
+                       break;
+               case INT13_EXTENDED_VERIFY:
+                       status = int13_extended_verify ( sandev, ix86 );
+                       break;
+               case INT13_EXTENDED_SEEK:
+                       status = int13_extended_seek ( sandev, ix86 );
+                       break;
+               case INT13_GET_EXTENDED_PARAMETERS:
+                       status = int13_get_extended_parameters ( sandev, ix86 );
+                       break;
+               case INT13_CDROM_STATUS_TERMINATE:
+                       status = int13_cdrom_status_terminate ( sandev, ix86 );
+                       break;
+               case INT13_CDROM_READ_BOOT_CATALOG:
+                       status = int13_cdrom_read_boot_catalog ( sandev, ix86 );
+                       break;
+               default:
+                       DBGC2 ( sandev, "*** Unrecognised INT13 ***\n" );
+                       status = -INT13_STATUS_INVALID;
+                       break;
+               }
+
+               /* Store status for INT 13,01 */
+               int13->last_status = status;
+
+               /* Negative status indicates an error */
+               if ( status < 0 ) {
+                       status = -status;
+                       DBGC ( sandev, "INT13,%02x (%02x) failed with status "
+                              "%02x\n", ix86->regs.ah, sandev->drive, status );
+               } else {
+                       ix86->flags &= ~CF;
+               }
+               ix86->regs.ah = status;
+
+               /* Set OF to indicate to wrapper not to chain this call */
+               ix86->flags |= OF;
+
+               return;
+       }
+}
+
+/**
+ * Hook INT 13 handler
+ *
+ */
+static void int13_hook_vector ( void ) {
+       /* Assembly wrapper to call int13().  int13() sets OF if we
+        * should not chain to the previous handler.  (The wrapper
+        * clears CF and OF before calling int13()).
+        */
+       __asm__  __volatile__ (
+              TEXT16_CODE ( "\nint13_wrapper:\n\t"
+                            /* Preserve %ax and %dx for future reference */
+                            "pushw %%bp\n\t"
+                            "movw %%sp, %%bp\n\t"                           
+                            "pushw %%ax\n\t"
+                            "pushw %%dx\n\t"
+                            /* Clear OF, set CF, call int13() */
+                            "orb $0, %%al\n\t" 
+                            "stc\n\t"
+                            VIRT_CALL ( int13 )
+                            /* Chain if OF not set */
+                            "jo 1f\n\t"
+                            "pushfw\n\t"
+                            "lcall *%%cs:int13_vector\n\t"
+                            "\n1:\n\t"
+                            /* Overwrite flags for iret */
+                            "pushfw\n\t"
+                            "popw 6(%%bp)\n\t"
+                            /* Fix up %dl:
+                             *
+                             * INT 13,15 : do nothing if hard disk
+                             * INT 13,08 : load with number of drives
+                             * all others: restore original value
+                             */
+                            "cmpb $0x15, -1(%%bp)\n\t"
+                            "jne 2f\n\t"
+                            "testb $0x80, -4(%%bp)\n\t"
+                            "jnz 3f\n\t"
+                            "\n2:\n\t"
+                            "movb -4(%%bp), %%dl\n\t"
+                            "cmpb $0x08, -1(%%bp)\n\t"
+                            "jne 3f\n\t"
+                            "testb $0x80, %%dl\n\t"
+                            "movb %%cs:num_drives, %%dl\n\t"
+                            "jnz 3f\n\t"
+                            "movb %%cs:num_fdds, %%dl\n\t"
+                            /* Return */
+                            "\n3:\n\t"
+                            "movw %%bp, %%sp\n\t"
+                            "popw %%bp\n\t"
+                            "iret\n\t" ) : : );
+
+       hook_bios_interrupt ( 0x13, ( intptr_t ) int13_wrapper, &int13_vector );
+}
+
+
+
+/**
+ * Load and verify master boot record from INT 13 drive
+ *
+ * @v drive            Drive number
+ * @v address          Boot code address to fill in
+ * @ret rc             Return status code
+ */
+static int int13_load_mbr ( unsigned int drive, struct segoff *address ) {
+       uint16_t status;
+       int discard_b, discard_c, discard_d;
+       uint16_t magic;
+
+       /* Use INT 13, 02 to read the MBR */
+       address->segment = 0;
+       address->offset = 0x7c00;
+       __asm__ __volatile__ ( REAL_CODE ( "pushw %%es\n\t"
+                                          "pushl %%ebx\n\t"
+                                          "popw %%bx\n\t"
+                                          "popw %%es\n\t"
+                                          "stc\n\t"
+                                          "sti\n\t"
+                                          "int $0x13\n\t"
+                                          "sti\n\t" /* BIOS bugs */
+                                          "jc 1f\n\t"
+                                          "xorw %%ax, %%ax\n\t"
+                                          "\n1:\n\t"
+                                          "popw %%es\n\t" )
+                              : "=a" ( status ), "=b" ( discard_b ),
+                                "=c" ( discard_c ), "=d" ( discard_d )
+                              : "a" ( 0x0201 ), "b" ( *address ),
+                                "c" ( 1 ), "d" ( drive ) );
+       if ( status ) {
+               DBG ( "INT13 drive %02x could not read MBR (status %04x)\n",
+                     drive, status );
+               return -EIO;
+       }
+
+       /* Check magic signature */
+       get_real ( magic, address->segment,
+                  ( address->offset +
+                    offsetof ( struct master_boot_record, magic ) ) );
+       if ( magic != INT13_MBR_MAGIC ) {
+               DBG ( "INT13 drive %02x does not contain a valid MBR\n",
+                     drive );
+               return -ENOEXEC;
+       }
+
+       return 0;
+}
+
+/** El Torito boot catalog command packet */
+static struct int13_cdrom_boot_catalog_command __data16 ( eltorito_cmd ) = {
+       .size = sizeof ( struct int13_cdrom_boot_catalog_command ),
+       .count = 1,
+       .buffer = 0x7c00,
+       .start = 0,
+};
+#define eltorito_cmd __use_data16 ( eltorito_cmd )
+
+/** El Torito disk address packet */
+static struct int13_disk_address __bss16 ( eltorito_address );
+#define eltorito_address __use_data16 ( eltorito_address )
+
+/**
+ * Load and verify El Torito boot record from INT 13 drive
+ *
+ * @v drive            Drive number
+ * @v address          Boot code address to fill in
+ * @ret rc             Return status code
+ */
+static int int13_load_eltorito ( unsigned int drive, struct segoff *address ) {
+       struct {
+               struct eltorito_validation_entry valid;
+               struct eltorito_boot_entry boot;
+       } __attribute__ (( packed )) catalog;
+       uint16_t status;
+    
+    if (g_sandev && g_sandev->drive == drive)
+    {
+        copy_to_user(phys_to_user ( eltorito_cmd.buffer ), 0, g_sandev->boot_catalog_sector, sizeof(g_sandev->boot_catalog_sector));
+    }
+    else
+    {
+       /* Use INT 13, 4d to read the boot catalog */
+       __asm__ __volatile__ ( REAL_CODE ( "stc\n\t"
+                                          "sti\n\t"
+                                          "int $0x13\n\t"
+                                          "sti\n\t" /* BIOS bugs */
+                                          "jc 1f\n\t"
+                                          "xorw %%ax, %%ax\n\t"
+                                          "\n1:\n\t" )
+                              : "=a" ( status )
+                              : "a" ( 0x4d00 ), "d" ( drive ),
+                                "S" ( __from_data16 ( &eltorito_cmd ) ) );
+       if ( status ) {
+               DBG ( "INT13 drive %02x could not read El Torito boot catalog "
+                     "(status %04x)\n", drive, status );
+               return -EIO;
+       }
+    }
+    
+       copy_from_user ( &catalog, phys_to_user ( eltorito_cmd.buffer ), 0,
+                        sizeof ( catalog ) );
+
+       /* Sanity checks */
+       if ( catalog.valid.platform_id != ELTORITO_PLATFORM_X86 ) {
+               DBG ( "INT13 drive %02x El Torito specifies unknown platform "
+                     "%02x\n", drive, catalog.valid.platform_id );
+               return -ENOEXEC;
+       }
+       if ( catalog.boot.indicator != ELTORITO_BOOTABLE ) {
+               DBG ( "INT13 drive %02x El Torito is not bootable\n", drive );
+               return -ENOEXEC;
+       }
+       if ( catalog.boot.media_type != ELTORITO_NO_EMULATION ) {
+               DBG ( "INT13 drive %02x El Torito requires emulation "
+                      "type %02x\n", drive, catalog.boot.media_type );
+               return -ENOTSUP;
+       }
+       DBG ( "INT13 drive %02x El Torito boot image at LBA %08x (count %d)\n",
+             drive, catalog.boot.start, catalog.boot.length );
+       address->segment = ( catalog.boot.load_segment ?
+                            catalog.boot.load_segment : 0x7c0 );
+       address->offset = 0;
+       DBG ( "INT13 drive %02x El Torito boot image loads at %04x:%04x\n",
+             drive, address->segment, address->offset );
+
+       /* Use INT 13, 42 to read the boot image */
+       eltorito_address.bufsize =
+               offsetof ( typeof ( eltorito_address ), buffer_phys );
+       eltorito_address.count = catalog.boot.length;
+       eltorito_address.buffer = *address;
+       eltorito_address.lba = catalog.boot.start;
+       __asm__ __volatile__ ( REAL_CODE ( "stc\n\t"
+                                          "sti\n\t"
+                                          "int $0x13\n\t"
+                                          "sti\n\t" /* BIOS bugs */
+                                          "jc 1f\n\t"
+                                          "xorw %%ax, %%ax\n\t"
+                                          "\n1:\n\t" )
+                              : "=a" ( status )
+                              : "a" ( 0x4200 ), "d" ( drive ),
+                                "S" ( __from_data16 ( &eltorito_address ) ) );
+       if ( status ) {
+               DBG ( "INT13 drive %02x could not read El Torito boot image "
+                     "(status %04x)\n", drive, status );
+               return -EIO;
+       }
+
+       return 0;
+}
+
+/**
+ * Hook INT 13 SAN device
+ *
+ * @v drive            Drive number
+ * @v uris             List of URIs
+ * @v count            Number of URIs
+ * @v flags            Flags
+ * @ret drive          Drive number, or negative error
+ *
+ * Registers the drive with the INT 13 emulation subsystem, and hooks
+ * the INT 13 interrupt vector (if not already hooked).
+ */
+unsigned int ventoy_int13_hook (ventoy_chain_head *chain) 
+{
+       unsigned int blocks;
+       unsigned int blocks_per_cyl;
+       unsigned int natural_drive;
+    struct int13_data *int13;
+
+       /* We simulate a cdrom, so no need to sync hd drive number */
+       //int13_sync_num_drives();
+
+    /* hook will copy num_drives to dl when int13 08 was called, so must initialize it's value */
+       get_real(num_drives, BDA_SEG, BDA_NUM_DRIVES);
+    
+       //natural_drive = num_drives | 0x80;
+       natural_drive = 0xE0; /* just set a cdrom drive number 224 */
+
+    if (chain->disk_drive >= 0x80 && chain->drive_map >= 0x80)
+    {
+        g_drive_map1 = chain->disk_drive;
+        g_drive_map2 = chain->drive_map;
+    }
+
+    /* a fake sandev */
+    g_sandev = zalloc(sizeof(struct san_device) + sizeof(struct int13_data));
+    g_sandev->priv = int13 = (struct int13_data *)(g_sandev + 1);
+    g_sandev->drive = int13->natural_drive = natural_drive;
+    g_sandev->is_cdrom = 1;
+    g_sandev->blksize_shift = 2;
+    g_sandev->capacity.blksize = 512;
+    g_sandev->capacity.blocks = chain->virt_img_size_in_bytes / 512;
+    g_sandev->exdrive = chain->disk_drive;
+    int13->boot_catalog = chain->boot_catalog;
+    memcpy(g_sandev->boot_catalog_sector, chain->boot_catalog_sector, sizeof(chain->boot_catalog_sector));
+
+
+    /* Apply guesses if no geometry already specified */
+       if ( ! int13->heads )
+               int13->heads = 255;
+       if ( ! int13->sectors_per_track )
+               int13->sectors_per_track = 63;
+       if ( ! int13->cylinders ) {
+               /* Avoid attempting a 64-bit divide on a 32-bit system */
+               blocks = int13_capacity32 ( g_sandev );
+               blocks_per_cyl = ( int13->heads * int13->sectors_per_track );
+
+               int13->cylinders = ( blocks / blocks_per_cyl );
+               if ( int13->cylinders > 1024 )
+                       int13->cylinders = 1024;
+       }
+
+       /* Hook INT 13 vector if not already hooked */
+       int13_hook_vector();
+
+       /* Update BIOS drive count */
+       //int13_sync_num_drives();
+
+       return natural_drive;
+}
+
+static uint8_t __bss16_array ( xbftab, [512 + 128])
+        __attribute__ (( aligned ( 16 ) ));
+#define xbftab __use_data16 ( xbftab )
+
+void * ventoy_get_runtime_addr(void)
+{
+    return (void *)user_to_phys((userptr_t)(&xbftab), 0);
+}
+
+/**
+ * Attempt to boot from an INT 13 drive
+ *
+ * @v drive            Drive number
+ * @v filename         Filename (or NULL to use default)
+ * @ret rc             Return status code
+ *
+ * This boots from the specified INT 13 drive by loading the Master
+ * Boot Record to 0000:7c00 and jumping to it.  INT 18 is hooked to
+ * capture an attempt by the MBR to boot the next device.  (This is
+ * the closest thing to a return path from an MBR).
+ *
+ * Note that this function can never return success, by definition.
+ */
+int ventoy_int13_boot ( unsigned int drive, void *imginfo, const char *cmdline) {
+       //struct memory_map memmap;
+       int rc;
+    int headlen;
+       struct segoff address;
+    struct acpi_header *acpi = NULL; 
+    struct ibft_table *ibft = NULL;
+        
+       /* Look for a usable boot sector */
+       if ( ( ( rc = int13_load_eltorito ( drive, &address ) ) != 0 ) &&
+        ( ( rc = int13_load_mbr ( drive, &address ) ) != 0 ))
+               return rc;
+
+    if (imginfo)
+    {
+        if (strstr(cmdline, "ibft"))
+        {
+            headlen = 80;
+            ibft = (struct ibft_table *)(&xbftab);
+            acpi = &(ibft->acpi);
+            memset(ibft, 0, headlen);
+
+            acpi->signature = IBFT_SIG;
+            acpi->length = headlen + sizeof(ventoy_os_param);
+            acpi->revision = 1;
+            strncpy(acpi->oem_id, "ventoy", sizeof(acpi->oem_id));
+           strncpy(acpi->oem_table_id, "runtime", sizeof(acpi->oem_table_id));
+            memcpy((uint8_t *)ibft + headlen, imginfo, sizeof(ventoy_os_param));
+            acpi_fix_checksum(acpi);
+        }
+        else
+        {
+            memcpy((&xbftab), imginfo, sizeof(ventoy_os_param));
+        }
+    }
+
+       /* Dump out memory map prior to boot, if memmap debugging is
+        * enabled.  Not required for program flow, but we have so
+        * many problems that turn out to be memory-map related that
+        * it's worth doing.
+        */
+       //get_memmap ( &memmap );
+
+    DBGC(g_sandev, "start to boot ...\n");
+
+       /* Jump to boot sector */
+       if ( ( rc = call_bootsector ( address.segment, address.offset,
+                                     drive ) ) != 0 ) {
+               return rc;
+       }
+
+       return -ECANCELED; /* -EIMPOSSIBLE */
+}
+
diff --git a/IPXE/ipxe-3fe683e/src/arch/x86/interface/pcbios/ventoy_int13.h b/IPXE/ipxe-3fe683e/src/arch/x86/interface/pcbios/ventoy_int13.h
new file mode 100644 (file)
index 0000000..e6fe545
--- /dev/null
@@ -0,0 +1,48 @@
+
+#ifndef __VENTOY_INT13_H__
+#define __VENTOY_INT13_H__
+
+#undef for_each_sandev
+#define for_each_sandev( sandev ) sandev = g_sandev; if (sandev)
+
+int ventoy_vdisk_read(struct san_device*sandev, uint64_t lba, unsigned int count, unsigned long buffer);
+
+static inline int ventoy_sandev_write ( struct san_device *sandev, uint64_t lba, unsigned int count, unsigned long buffer )
+{
+    (void)sandev;
+    (void)lba;
+    (void)count;
+    (void)buffer;
+    DBGC(sandev, "ventoy_sandev_write\n");
+    return 0;
+}
+
+static inline int ventoy_sandev_reset (void *sandev)
+{
+    (void)sandev;
+    DBGC(sandev, "ventoy_sandev_reset\n");
+    return 0;
+}
+
+#define sandev_reset  ventoy_sandev_reset
+#define sandev_read   ventoy_vdisk_read
+#define sandev_write  ventoy_sandev_write
+
+#undef  ECANCELED
+#define ECANCELED 0x0b
+#undef  ENODEV
+#define ENODEV 0x2c
+#undef  ENOTSUP
+#define ENOTSUP 0x3c
+#undef  ENOMEM
+#define ENOMEM 0x31
+#undef  EIO
+#define EIO 0x1d
+#undef  ENOEXEC
+#define ENOEXEC 0x2e
+#undef  ENOSPC
+#define ENOSPC 0x34
+
+
+#endif /* __VENTOY_INT13_H__ */
+
diff --git a/IPXE/ipxe-3fe683e/src/config/settings.h b/IPXE/ipxe-3fe683e/src/config/settings.h
new file mode 100644 (file)
index 0000000..8c22e1f
--- /dev/null
@@ -0,0 +1,24 @@
+#ifndef CONFIG_SETTINGS_H
+#define CONFIG_SETTINGS_H
+
+/** @file
+ *
+ * Configuration settings sources
+ *
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+//#define      PCI_SETTINGS    /* PCI device settings */
+//#define      CPUID_SETTINGS  /* CPUID settings */
+//#define      MEMMAP_SETTINGS /* Memory map settings */
+//#define      VMWARE_SETTINGS /* VMware GuestInfo settings */
+//#define      VRAM_SETTINGS   /* Video RAM dump settings */
+//#define      ACPI_SETTINGS   /* ACPI settings */
+
+#include <config/named.h>
+#include NAMED_CONFIG(settings.h)
+#include <config/local/settings.h>
+#include LOCAL_NAMED_CONFIG(settings.h)
+
+#endif /* CONFIG_SETTINGS_H */
diff --git a/IPXE/ipxe-3fe683e/src/core/device.c b/IPXE/ipxe-3fe683e/src/core/device.c
new file mode 100644 (file)
index 0000000..d3909b4
--- /dev/null
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <string.h>
+#include <ipxe/list.h>
+#include <ipxe/tables.h>
+#include <ipxe/init.h>
+#include <ipxe/interface.h>
+#include <ipxe/device.h>
+
+/**
+ * @file
+ *
+ * Device model
+ *
+ */
+
+/** Registered root devices */
+static LIST_HEAD ( devices );
+
+/** Device removal inhibition counter */
+int device_keep_count = 0;
+
+/**
+ * Probe a root device
+ *
+ * @v rootdev          Root device
+ * @ret rc             Return status code
+ */
+static int rootdev_probe ( struct root_device *rootdev ) {
+       int rc;
+
+       DBG ( "Adding %s root bus\n", rootdev->dev.name );
+       if ( ( rc = rootdev->driver->probe ( rootdev ) ) != 0 ) {
+               DBG ( "Failed to add %s root bus: %s\n",
+                     rootdev->dev.name, strerror ( rc ) );
+               return rc;
+       }
+
+       return 0;
+}
+
+/**
+ * Remove a root device
+ *
+ * @v rootdev          Root device
+ */
+static void rootdev_remove ( struct root_device *rootdev ) {
+       rootdev->driver->remove ( rootdev );
+       DBG ( "Removed %s root bus\n", rootdev->dev.name );
+}
+
+/**
+ * Probe all devices
+ *
+ * This initiates probing for all devices in the system.  After this
+ * call, the device hierarchy will be populated, and all hardware
+ * should be ready to use.
+ */
+static void probe_devices ( void ) {
+       struct root_device *rootdev;
+       int rc;
+
+       for_each_table_entry ( rootdev, ROOT_DEVICES ) {
+               list_add ( &rootdev->dev.siblings, &devices );
+               INIT_LIST_HEAD ( &rootdev->dev.children );
+               if ( ( rc = rootdev_probe ( rootdev ) ) != 0 )
+                       list_del ( &rootdev->dev.siblings );
+       }
+}
+
+/**
+ * Remove all devices
+ *
+ */
+static void remove_devices ( int booting __unused ) {
+       struct root_device *rootdev;
+       struct root_device *tmp;
+
+       if ( device_keep_count != 0 ) {
+               DBG ( "Refusing to remove devices on shutdown\n" );
+               return;
+       }
+
+       list_for_each_entry_safe ( rootdev, tmp, &devices, dev.siblings ) {
+               rootdev_remove ( rootdev );
+               list_del ( &rootdev->dev.siblings );
+       }
+}
+
+//struct startup_fn startup_devices __startup_fn ( STARTUP_NORMAL ) = {
+struct startup_fn startup_devices  = {
+       .name = "devices",
+       .startup = probe_devices,
+       .shutdown = remove_devices,
+};
+
+/**
+ * Identify a device behind an interface
+ *
+ * @v intf             Interface
+ * @ret device         Device, or NULL
+ */
+struct device * identify_device ( struct interface *intf ) {
+       struct interface *dest;
+       identify_device_TYPE ( void * ) *op =
+               intf_get_dest_op ( intf, identify_device, &dest );
+       void *object = intf_object ( dest );
+       void *device;
+
+       if ( op ) {
+               device = op ( object );
+       } else {
+               /* Default is to return NULL */
+               device = NULL;
+       }
+
+       intf_put ( dest );
+       return device;
+}
diff --git a/IPXE/ipxe-3fe683e/src/core/main.c b/IPXE/ipxe-3fe683e/src/core/main.c
new file mode 100644 (file)
index 0000000..6a60ad3
--- /dev/null
@@ -0,0 +1,47 @@
+/**************************************************************************
+iPXE -  Network Bootstrap Program
+
+Literature dealing with the network protocols:
+       ARP - RFC826
+       RARP - RFC903
+       UDP - RFC768
+       BOOTP - RFC951, RFC2132 (vendor extensions)
+       DHCP - RFC2131, RFC2132 (options)
+       TFTP - RFC1350, RFC2347 (options), RFC2348 (blocksize), RFC2349 (tsize)
+       RPC - RFC1831, RFC1832 (XDR), RFC1833 (rpcbind/portmapper)
+
+**************************************************************************/
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stddef.h>
+#include <stdio.h>
+#include <ipxe/init.h>
+#include <ipxe/version.h>
+#include <usr/autoboot.h>
+#include <ventoy.h>
+
+/**
+ * Main entry point
+ *
+ * @ret rc             Return status code
+ */
+__asmcall int main ( void ) {
+       int rc;
+
+       /* Perform one-time-only initialisation (e.g. heap) */
+       initialise();
+
+       /* Some devices take an unreasonably long time to initialise */
+       //printf ( "%s initialising devices...", product_short_name );
+       startup();
+       //printf ( "ok\n" );
+
+       /* Attempt to boot */
+       if ( ( rc = ventoy_boot_vdisk ( NULL ) ) != 0 )
+               goto err_ipxe;
+
+ err_ipxe:
+       shutdown_exit();
+       return rc;
+}
diff --git a/IPXE/ipxe-3fe683e/src/core/ventoy_dummy.c b/IPXE/ipxe-3fe683e/src/core/ventoy_dummy.c
new file mode 100644 (file)
index 0000000..ee7d114
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * Copyright (C) 2017 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+/**
+ * @file
+ *
+ * SAN booting
+ *
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <assert.h>
+#include <ipxe/xfer.h>
+#include <ipxe/open.h>
+#include <ipxe/timer.h>
+#include <ipxe/process.h>
+#include <ipxe/iso9660.h>
+#include <ipxe/dhcp.h>
+#include <ipxe/settings.h>
+#include <ipxe/quiesce.h>
+#include <ipxe/sanboot.h>
+#include <ipxe/pci.h>
+#include <ipxe/scsi.h>
+#include <ipxe/ata.h>
+#include <ipxe/acpi.h>
+#include <ipxe/ibft.h>
+
+unsigned long pci_bar_size ( struct pci_device *pci, unsigned int reg )
+{
+    (void)pci;
+    (void)reg;
+    
+    return 0;
+}
+
+unsigned long pci_bar_start ( struct pci_device *pci, unsigned int reg )
+{
+    (void)pci;
+    (void)reg;
+    return 0;
+}
+
+void adjust_pci_device ( struct pci_device *pci )
+{
+    (void)pci;
+}
+
+/*
+ * obj_netfront
+ * obj_netvsc
+ * 
+ * ibft_model
+ */
+
+int scsi_parse_lun ( const char *lun_string, struct scsi_lun *lun )
+{
+    (void)lun_string;
+    (void)lun;
+    return 0;
+}
+
+void scsi_parse_sense ( const void *data, size_t len,
+                              struct scsi_sns_descriptor *sense )
+{
+    (void)data;
+    (void)len;
+    (void)sense;
+    
+    return;
+}
+
+void scsi_response ( struct interface *intf, struct scsi_rsp *response )
+{
+    (void)intf;
+    (void)response;
+}
+int scsi_open ( struct interface *block, struct interface *scsi,
+                      struct scsi_lun *lun )
+{
+    (void)block;
+    (void)scsi;
+    (void)lun;
+
+    return 0;
+}
+
+int scsi_command ( struct interface *control, struct interface *data,
+                         struct scsi_cmd *command )
+{
+    (void)control;
+    (void)data;
+    (void)command;
+    
+    return 0;
+}
+               
+
+int ata_open ( struct interface *block, struct interface *ata,
+                     unsigned int device, unsigned int max_count )
+{
+    (void)block;
+    (void)ata;
+    (void)device;
+    (void)max_count;
+    
+    return 0;
+}
+
+int ata_command ( struct interface *control, struct interface *data,
+                        struct ata_cmd *command )
+{
+    (void)control;
+    (void)data;
+    (void)command;
+
+    return 0;
+}
+
+#if 0
+static uint32_t mCrcTable[256] = 
+{
+    0x00000000, 0x77073096, 0xEE0E612C, 0x990951BA, 0x076DC419, 0x706AF48F, 0xE963A535, 0x9E6495A3, 
+    0x0EDB8832, 0x79DCB8A4, 0xE0D5E91E, 0x97D2D988, 0x09B64C2B, 0x7EB17CBD, 0xE7B82D07, 0x90BF1D91, 
+    0x1DB71064, 0x6AB020F2, 0xF3B97148, 0x84BE41DE, 0x1ADAD47D, 0x6DDDE4EB, 0xF4D4B551, 0x83D385C7, 
+    0x136C9856, 0x646BA8C0, 0xFD62F97A, 0x8A65C9EC, 0x14015C4F, 0x63066CD9, 0xFA0F3D63, 0x8D080DF5, 
+    0x3B6E20C8, 0x4C69105E, 0xD56041E4, 0xA2677172, 0x3C03E4D1, 0x4B04D447, 0xD20D85FD, 0xA50AB56B, 
+    0x35B5A8FA, 0x42B2986C, 0xDBBBC9D6, 0xACBCF940, 0x32D86CE3, 0x45DF5C75, 0xDCD60DCF, 0xABD13D59, 
+    0x26D930AC, 0x51DE003A, 0xC8D75180, 0xBFD06116, 0x21B4F4B5, 0x56B3C423, 0xCFBA9599, 0xB8BDA50F, 
+    0x2802B89E, 0x5F058808, 0xC60CD9B2, 0xB10BE924, 0x2F6F7C87, 0x58684C11, 0xC1611DAB, 0xB6662D3D, 
+    0x76DC4190, 0x01DB7106, 0x98D220BC, 0xEFD5102A, 0x71B18589, 0x06B6B51F, 0x9FBFE4A5, 0xE8B8D433, 
+    0x7807C9A2, 0x0F00F934, 0x9609A88E, 0xE10E9818, 0x7F6A0DBB, 0x086D3D2D, 0x91646C97, 0xE6635C01, 
+    0x6B6B51F4, 0x1C6C6162, 0x856530D8, 0xF262004E, 0x6C0695ED, 0x1B01A57B, 0x8208F4C1, 0xF50FC457, 
+    0x65B0D9C6, 0x12B7E950, 0x8BBEB8EA, 0xFCB9887C, 0x62DD1DDF, 0x15DA2D49, 0x8CD37CF3, 0xFBD44C65, 
+    0x4DB26158, 0x3AB551CE, 0xA3BC0074, 0xD4BB30E2, 0x4ADFA541, 0x3DD895D7, 0xA4D1C46D, 0xD3D6F4FB, 
+    0x4369E96A, 0x346ED9FC, 0xAD678846, 0xDA60B8D0, 0x44042D73, 0x33031DE5, 0xAA0A4C5F, 0xDD0D7CC9, 
+    0x5005713C, 0x270241AA, 0xBE0B1010, 0xC90C2086, 0x5768B525, 0x206F85B3, 0xB966D409, 0xCE61E49F, 
+    0x5EDEF90E, 0x29D9C998, 0xB0D09822, 0xC7D7A8B4, 0x59B33D17, 0x2EB40D81, 0xB7BD5C3B, 0xC0BA6CAD, 
+    0xEDB88320, 0x9ABFB3B6, 0x03B6E20C, 0x74B1D29A, 0xEAD54739, 0x9DD277AF, 0x04DB2615, 0x73DC1683, 
+    0xE3630B12, 0x94643B84, 0x0D6D6A3E, 0x7A6A5AA8, 0xE40ECF0B, 0x9309FF9D, 0x0A00AE27, 0x7D079EB1, 
+    0xF00F9344, 0x8708A3D2, 0x1E01F268, 0x6906C2FE, 0xF762575D, 0x806567CB, 0x196C3671, 0x6E6B06E7, 
+    0xFED41B76, 0x89D32BE0, 0x10DA7A5A, 0x67DD4ACC, 0xF9B9DF6F, 0x8EBEEFF9, 0x17B7BE43, 0x60B08ED5, 
+    0xD6D6A3E8, 0xA1D1937E, 0x38D8C2C4, 0x4FDFF252, 0xD1BB67F1, 0xA6BC5767, 0x3FB506DD, 0x48B2364B, 
+    0xD80D2BDA, 0xAF0A1B4C, 0x36034AF6, 0x41047A60, 0xDF60EFC3, 0xA867DF55, 0x316E8EEF, 0x4669BE79, 
+    0xCB61B38C, 0xBC66831A, 0x256FD2A0, 0x5268E236, 0xCC0C7795, 0xBB0B4703, 0x220216B9, 0x5505262F, 
+    0xC5BA3BBE, 0xB2BD0B28, 0x2BB45A92, 0x5CB36A04, 0xC2D7FFA7, 0xB5D0CF31, 0x2CD99E8B, 0x5BDEAE1D, 
+    0x9B64C2B0, 0xEC63F226, 0x756AA39C, 0x026D930A, 0x9C0906A9, 0xEB0E363F, 0x72076785, 0x05005713, 
+    0x95BF4A82, 0xE2B87A14, 0x7BB12BAE, 0x0CB61B38, 0x92D28E9B, 0xE5D5BE0D, 0x7CDCEFB7, 0x0BDBDF21, 
+    0x86D3D2D4, 0xF1D4E242, 0x68DDB3F8, 0x1FDA836E, 0x81BE16CD, 0xF6B9265B, 0x6FB077E1, 0x18B74777, 
+    0x88085AE6, 0xFF0F6A70, 0x66063BCA, 0x11010B5C, 0x8F659EFF, 0xF862AE69, 0x616BFFD3, 0x166CCF45, 
+    0xA00AE278, 0xD70DD2EE, 0x4E048354, 0x3903B3C2, 0xA7672661, 0xD06016F7, 0x4969474D, 0x3E6E77DB, 
+    0xAED16A4A, 0xD9D65ADC, 0x40DF0B66, 0x37D83BF0, 0xA9BCAE53, 0xDEBB9EC5, 0x47B2CF7F, 0x30B5FFE9, 
+    0xBDBDF21C, 0xCABAC28A, 0x53B39330, 0x24B4A3A6, 0xBAD03605, 0xCDD70693, 0x54DE5729, 0x23D967BF, 
+    0xB3667A2E, 0xC4614AB8, 0x5D681B02, 0x2A6F2B94, 0xB40BBE37, 0xC30C8EA1, 0x5A05DF1B, 0x2D02EF8D
+};
+
+uint32_t CalculateCrc32
+(
+    const void       *Buffer,
+    uint32_t          Length,
+    uint32_t          InitValue
+)
+{
+    uint32_t Crc = 0;
+    uint32_t Index = 0;
+    const uint8_t *Ptr = (const uint8_t *)Buffer;
+
+    Crc = InitValue ^ 0xffffffff;
+    for (Index = 0; Index < Length; Index++, Ptr++)
+    {
+        Crc = (Crc >> 8) ^ mCrcTable[(uint8_t) Crc ^ *Ptr];
+    }
+
+    return (Crc ^ 0xffffffff);
+}
+
+#endif
+
diff --git a/IPXE/ipxe-3fe683e/src/core/vsprintf.c b/IPXE/ipxe-3fe683e/src/core/vsprintf.c
new file mode 100644 (file)
index 0000000..4a231b4
--- /dev/null
@@ -0,0 +1,501 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stddef.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <errno.h>
+#include <wchar.h>
+#include <ipxe/vsprintf.h>
+
+/** @file */
+
+#define CHAR_LEN       0       /**< "hh" length modifier */
+#define SHORT_LEN      1       /**< "h" length modifier */
+#define INT_LEN                2       /**< no length modifier */
+#define LONG_LEN       3       /**< "l" length modifier */
+#define LONGLONG_LEN   4       /**< "ll" length modifier */
+#define SIZE_T_LEN     5       /**< "z" length modifier */
+
+static uint8_t type_sizes[] = {
+       [CHAR_LEN]      = sizeof ( char ),
+       [SHORT_LEN]     = sizeof ( short ),
+       [INT_LEN]       = sizeof ( int ),
+       [LONG_LEN]      = sizeof ( long ),
+       [LONGLONG_LEN]  = sizeof ( long long ),
+       [SIZE_T_LEN]    = sizeof ( size_t ),
+};
+
+/**
+ * Use lower-case for hexadecimal digits
+ *
+ * Note that this value is set to 0x20 since that makes for very
+ * efficient calculations.  (Bitwise-ORing with @c LCASE converts to a
+ * lower-case character, for example.)
+ */
+#define LCASE 0x20
+
+/**
+ * Use "alternate form"
+ *
+ * For hexadecimal numbers, this means to add a "0x" or "0X" prefix to
+ * the number.
+ */
+#define ALT_FORM 0x02
+
+/**
+ * Use zero padding
+ *
+ * Note that this value is set to 0x10 since that allows the pad
+ * character to be calculated as @c 0x20|(flags&ZPAD)
+ */
+#define ZPAD 0x10
+
+/**
+ * Format a hexadecimal number
+ *
+ * @v end              End of buffer to contain number
+ * @v num              Number to format
+ * @v width            Minimum field width
+ * @v flags            Format flags
+ * @ret ptr            End of buffer
+ *
+ * Fills a buffer in reverse order with a formatted hexadecimal
+ * number.  The number will be zero-padded to the specified width.
+ * Lower-case and "alternate form" (i.e. "0x" prefix) flags may be
+ * set.
+ *
+ * There must be enough space in the buffer to contain the largest
+ * number that this function can format.
+ */
+static char * format_hex ( char *end, unsigned long long num, int width,
+                          int flags ) {
+       char *ptr = end;
+       int case_mod = ( flags & LCASE );
+       int pad = ( ( flags & ZPAD ) | ' ' );
+
+       /* Generate the number */
+       do {
+               *(--ptr) = "0123456789ABCDEF"[ num & 0xf ] | case_mod;
+               num >>= 4;
+       } while ( num );
+
+       /* Pad to width */
+       while ( ( end - ptr ) < width )
+               *(--ptr) = pad;
+
+       /* Add "0x" or "0X" if alternate form specified */
+       if ( flags & ALT_FORM ) {
+               *(--ptr) = 'X' | case_mod;
+               *(--ptr) = '0';
+       }
+
+       return ptr;
+}
+
+/**
+ * Format a decimal number
+ *
+ * @v end              End of buffer to contain number
+ * @v num              Number to format
+ * @v width            Minimum field width
+ * @v flags            Format flags
+ * @ret ptr            End of buffer
+ *
+ * Fills a buffer in reverse order with a formatted decimal number.
+ * The number will be space-padded to the specified width.
+ *
+ * There must be enough space in the buffer to contain the largest
+ * number that this function can format.
+ */
+static char * format_decimal ( char *end, signed long num, int width,
+                              int flags ) {
+       char *ptr = end;
+       int negative = 0;
+       int zpad = ( flags & ZPAD );
+       int pad = ( zpad | ' ' );
+
+       /* Generate the number */
+       if ( num < 0 ) {
+               negative = 1;
+               num = -num;
+       }
+       do {
+               *(--ptr) = '0' + ( num % 10 );
+               num /= 10;
+       } while ( num );
+
+       /* Add "-" if necessary */
+       if ( negative && ( ! zpad ) )
+               *(--ptr) = '-';
+
+       /* Pad to width */
+       while ( ( end - ptr ) < width )
+               *(--ptr) = pad;
+
+       /* Add "-" if necessary */
+       if ( negative && zpad )
+               *ptr = '-';
+
+       return ptr;
+}
+
+#define ZPAD 0x10
+char *format_unsigned_decimal(char *end, unsigned long long num, int width, int flags) 
+{
+       char *ptr = end;
+       int zpad = ( flags & ZPAD );
+       int pad = ( zpad | ' ' );
+
+       do {
+               *(--ptr) = '0' + ( num % 10 );
+               num /= 10;
+       } while ( num );
+
+       /* Pad to width */
+       while ( ( end - ptr ) < width )
+               *(--ptr) = pad;
+
+       return ptr;
+}
+
+/**
+ * Print character via a printf context
+ *
+ * @v ctx              Context
+ * @v c                        Character
+ *
+ * Call's the printf_context::handler() method and increments
+ * printf_context::len.
+ */
+static inline void cputchar ( struct printf_context *ctx, unsigned char c ) {
+       ctx->handler ( ctx, c );
+       ++ctx->len;
+}
+
+/**
+ * Write a formatted string to a printf context
+ *
+ * @v ctx              Context
+ * @v fmt              Format string
+ * @v args             Arguments corresponding to the format string
+ * @ret len            Length of formatted string
+ */
+size_t vcprintf ( struct printf_context *ctx, const char *fmt, va_list args ) {
+       int flags;
+       int width;
+       uint8_t *length;
+       char *ptr;
+       char tmp_buf[32]; /* 32 is enough for all numerical formats.
+                          * Insane width fields could overflow this buffer. */
+       wchar_t *wptr;
+
+       /* Initialise context */
+       ctx->len = 0;
+
+       for ( ; *fmt ; fmt++ ) {
+               /* Pass through ordinary characters */
+               if ( *fmt != '%' ) {
+                       cputchar ( ctx, *fmt );
+                       continue;
+               }
+               fmt++;
+               /* Process flag characters */
+               flags = 0;
+               for ( ; ; fmt++ ) {
+                       if ( *fmt == '#' ) {
+                               flags |= ALT_FORM;
+                       } else if ( *fmt == '0' ) {
+                               flags |= ZPAD;
+                       } else {
+                               /* End of flag characters */
+                               break;
+                       }
+               }
+               /* Process field width */
+               width = 0;
+               for ( ; ; fmt++ ) {
+                       if ( ( ( unsigned ) ( *fmt - '0' ) ) < 10 ) {
+                               width = ( width * 10 ) + ( *fmt - '0' );
+                       } else {
+                               break;
+                       }
+               }
+               /* We don't do floating point */
+               /* Process length modifier */
+               length = &type_sizes[INT_LEN];
+               for ( ; ; fmt++ ) {
+                       if ( *fmt == 'h' ) {
+                               length--;
+                       } else if ( *fmt == 'l' ) {
+                               length++;
+                       } else if ( *fmt == 'z' ) {
+                               length = &type_sizes[SIZE_T_LEN];
+                       } else {
+                               break;
+                       }
+               }
+               /* Process conversion specifier */
+               ptr = tmp_buf + sizeof ( tmp_buf ) - 1;
+               *ptr = '\0';
+               wptr = NULL;
+               if ( *fmt == 'c' ) {
+                       if ( length < &type_sizes[LONG_LEN] ) {
+                               cputchar ( ctx, va_arg ( args, unsigned int ) );
+                       } else {
+                               wchar_t wc;
+                               size_t len;
+
+                               wc = va_arg ( args, wint_t );
+                               len = wcrtomb ( tmp_buf, wc, NULL );
+                               tmp_buf[len] = '\0';
+                               ptr = tmp_buf;
+                       }
+               } else if ( *fmt == 's' ) {
+                       if ( length < &type_sizes[LONG_LEN] ) {
+                               ptr = va_arg ( args, char * );
+                               if ( ! ptr )
+                                       ptr = "<NULL>";
+                       } else {
+                               wptr = va_arg ( args, wchar_t * );
+                               if ( ! wptr )
+                                       ptr = "<NULL>";
+                       }
+               } else if ( *fmt == 'p' ) {
+                       intptr_t ptrval;
+
+                       ptrval = ( intptr_t ) va_arg ( args, void * );
+                       ptr = format_hex ( ptr, ptrval, width, 
+                                          ( ALT_FORM | LCASE ) );
+               } else if ( ( *fmt & ~0x20 ) == 'X' ) {
+                       unsigned long long hex;
+
+                       flags |= ( *fmt & 0x20 ); /* LCASE */
+                       if ( *length >= sizeof ( unsigned long long ) ) {
+                               hex = va_arg ( args, unsigned long long );
+                       } else if ( *length >= sizeof ( unsigned long ) ) {
+                               hex = va_arg ( args, unsigned long );
+                       } else {
+                               hex = va_arg ( args, unsigned int );
+                       }
+                       ptr = format_hex ( ptr, hex, width, flags );
+               } else if ( ( *fmt == 'd' ) || ( *fmt == 'i' ) ){
+                       signed long decimal;
+
+                       if ( *length >= sizeof ( signed long ) ) {
+                               decimal = va_arg ( args, signed long );
+                       } else {
+                               decimal = va_arg ( args, signed int );
+                       }
+                       ptr = format_decimal ( ptr, decimal, width, flags );
+               } else if ( ( *fmt == 'u' ) || ( *fmt == 'U' )){
+                       unsigned long long decimal;
+            if ( *length >= sizeof ( unsigned long long ) ) {
+                               decimal = va_arg ( args, unsigned long long );
+                       } else if ( *length >= sizeof ( unsigned long ) ) {
+                               decimal = va_arg ( args, unsigned long );
+                       } else {
+                               decimal = va_arg ( args, unsigned int );
+                       }
+                       ptr = format_unsigned_decimal( ptr, decimal, width, flags );
+               } else {
+                       *(--ptr) = *fmt;
+               }
+               /* Write out conversion result */
+               if ( wptr == NULL ) {
+                       for ( ; *ptr ; ptr++ ) {
+                               cputchar ( ctx, *ptr );
+                       }
+               } else {
+                       for ( ; *wptr ; wptr++ ) {
+                               size_t len = wcrtomb ( tmp_buf, *wptr, NULL );
+                               for ( ptr = tmp_buf ; len-- ; ptr++ ) {
+                                       cputchar ( ctx, *ptr );
+                               }
+                       }
+               }
+       }
+
+       return ctx->len;
+}
+
+/** Context used by vsnprintf() and friends */
+struct sputc_context {
+       struct printf_context ctx;
+       /** Buffer for formatted string (used by printf_sputc()) */
+       char *buf;
+       /** Buffer length (used by printf_sputc()) */
+       size_t max_len; 
+};
+
+/**
+ * Write character to buffer
+ *
+ * @v ctx              Context
+ * @v c                        Character
+ */
+static void printf_sputc ( struct printf_context *ctx, unsigned int c ) {
+       struct sputc_context * sctx =
+               container_of ( ctx, struct sputc_context, ctx );
+
+       if ( ctx->len < sctx->max_len )
+               sctx->buf[ctx->len] = c;
+}
+
+/**
+ * Write a formatted string to a buffer
+ *
+ * @v buf              Buffer into which to write the string
+ * @v size             Size of buffer
+ * @v fmt              Format string
+ * @v args             Arguments corresponding to the format string
+ * @ret len            Length of formatted string
+ *
+ * If the buffer is too small to contain the string, the returned
+ * length is the length that would have been written had enough space
+ * been available.
+ */
+int vsnprintf ( char *buf, size_t size, const char *fmt, va_list args ) {
+       struct sputc_context sctx;
+       size_t len;
+       size_t end;
+
+       /* Hand off to vcprintf */
+       sctx.ctx.handler = printf_sputc;
+       sctx.buf = buf;
+       sctx.max_len = size;
+       len = vcprintf ( &sctx.ctx, fmt, args );
+
+       /* Add trailing NUL */
+       if ( size ) {
+               end = size - 1;
+               if ( len < end )
+                       end = len;
+               buf[end] = '\0';
+       }
+
+       return len;
+}
+
+/**
+ * Write a formatted string to a buffer
+ *
+ * @v buf              Buffer into which to write the string
+ * @v size             Size of buffer
+ * @v fmt              Format string
+ * @v ...              Arguments corresponding to the format string
+ * @ret len            Length of formatted string
+ */
+int snprintf ( char *buf, size_t size, const char *fmt, ... ) {
+       va_list args;
+       int i;
+
+       va_start ( args, fmt );
+       i = vsnprintf ( buf, size, fmt, args );
+       va_end ( args );
+       return i;
+}
+
+/**
+ * Version of vsnprintf() that accepts a signed buffer size
+ *
+ * @v buf              Buffer into which to write the string
+ * @v size             Size of buffer
+ * @v fmt              Format string
+ * @v args             Arguments corresponding to the format string
+ * @ret len            Length of formatted string
+ */
+int vssnprintf ( char *buf, ssize_t ssize, const char *fmt, va_list args ) {
+
+       /* Treat negative buffer size as zero buffer size */
+       if ( ssize < 0 )
+               ssize = 0;
+
+       /* Hand off to vsnprintf */
+       return vsnprintf ( buf, ssize, fmt, args );
+}
+
+/**
+ * Version of vsnprintf() that accepts a signed buffer size
+ *
+ * @v buf              Buffer into which to write the string
+ * @v size             Size of buffer
+ * @v fmt              Format string
+ * @v ...              Arguments corresponding to the format string
+ * @ret len            Length of formatted string
+ */
+int ssnprintf ( char *buf, ssize_t ssize, const char *fmt, ... ) {
+       va_list args;
+       int len;
+
+       /* Hand off to vssnprintf */
+       va_start ( args, fmt );
+       len = vssnprintf ( buf, ssize, fmt, args );
+       va_end ( args );
+       return len;
+}
+
+/**
+ * Write character to console
+ *
+ * @v ctx              Context
+ * @v c                        Character
+ */
+static void printf_putchar ( struct printf_context *ctx __unused,
+                            unsigned int c ) {
+       putchar ( c );
+}
+
+/**
+ * Write a formatted string to the console
+ *
+ * @v fmt              Format string
+ * @v args             Arguments corresponding to the format string
+ * @ret len            Length of formatted string
+ */
+int vprintf ( const char *fmt, va_list args ) {
+       struct printf_context ctx;
+
+       /* Hand off to vcprintf */
+       ctx.handler = printf_putchar;   
+       return vcprintf ( &ctx, fmt, args );    
+}
+
+/**
+ * Write a formatted string to the console.
+ *
+ * @v fmt              Format string
+ * @v ...              Arguments corresponding to the format string
+ * @ret        len             Length of formatted string
+ */
+int printf ( const char *fmt, ... ) {
+       va_list args;
+       int i;
+
+       va_start ( args, fmt );
+       i = vprintf ( fmt, args );
+       va_end ( args );
+       return i;
+}
diff --git a/IPXE/ipxe-3fe683e/src/drivers/net/efi/snp.c b/IPXE/ipxe-3fe683e/src/drivers/net/efi/snp.c
new file mode 100644 (file)
index 0000000..8d15b6f
--- /dev/null
@@ -0,0 +1,122 @@
+/*
+ * Copyright (C) 2014 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#if 0
+
+#include <errno.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_driver.h>
+#include <ipxe/efi/efi_snp.h>
+#include "snpnet.h"
+#include "nii.h"
+
+/** @file
+ *
+ * SNP driver
+ *
+ */
+
+/**
+ * Check to see if driver supports a device
+ *
+ * @v device           EFI device handle
+ * @ret rc             Return status code
+ */
+static int snp_supported ( EFI_HANDLE device ) {
+       EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+       EFI_STATUS efirc;
+
+       /* Check that this is not a device we are providing ourselves */
+       if ( find_snpdev ( device ) != NULL ) {
+               DBGCP ( device, "SNP %s is provided by this binary\n",
+                       efi_handle_name ( device ) );
+               return -ENOTTY;
+       }
+
+       /* Test for presence of simple network protocol */
+       if ( ( efirc = bs->OpenProtocol ( device,
+                                         &efi_simple_network_protocol_guid,
+                                         NULL, efi_image_handle, device,
+                                         EFI_OPEN_PROTOCOL_TEST_PROTOCOL))!=0){
+               DBGCP ( device, "SNP %s is not an SNP device\n",
+                       efi_handle_name ( device ) );
+               return -EEFI ( efirc );
+       }
+       DBGC ( device, "SNP %s is an SNP device\n",
+              efi_handle_name ( device ) );
+
+       return 0;
+}
+
+/**
+ * Check to see if driver supports a device
+ *
+ * @v device           EFI device handle
+ * @ret rc             Return status code
+ */
+static int nii_supported ( EFI_HANDLE device ) {
+       EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+       EFI_STATUS efirc;
+
+       /* Check that this is not a device we are providing ourselves */
+       if ( find_snpdev ( device ) != NULL ) {
+               DBGCP ( device, "NII %s is provided by this binary\n",
+                       efi_handle_name ( device ) );
+               return -ENOTTY;
+       }
+
+       /* Test for presence of NII protocol */
+       if ( ( efirc = bs->OpenProtocol ( device,
+                                         &efi_nii31_protocol_guid,
+                                         NULL, efi_image_handle, device,
+                                         EFI_OPEN_PROTOCOL_TEST_PROTOCOL))!=0){
+               DBGCP ( device, "NII %s is not an NII device\n",
+                       efi_handle_name ( device ) );
+               return -EEFI ( efirc );
+       }
+       DBGC ( device, "NII %s is an NII device\n",
+              efi_handle_name ( device ) );
+
+       return 0;
+}
+
+/** EFI SNP driver */
+struct efi_driver snp_driver __efi_driver ( EFI_DRIVER_NORMAL ) = {
+       .name = "SNP",
+       .supported = snp_supported,
+       .start = snpnet_start,
+       .stop = snpnet_stop,
+};
+
+/** EFI NII driver */
+struct efi_driver nii_driver __efi_driver ( EFI_DRIVER_NORMAL ) = {
+       .name = "NII",
+       .supported = nii_supported,
+       .start = nii_start,
+       .stop = nii_stop,
+};
+
+#endif
+
diff --git a/IPXE/ipxe-3fe683e/src/include/ipxe/sanboot.h b/IPXE/ipxe-3fe683e/src/include/ipxe/sanboot.h
new file mode 100644 (file)
index 0000000..d9b9de4
--- /dev/null
@@ -0,0 +1,255 @@
+#ifndef _IPXE_SANBOOT_H
+#define _IPXE_SANBOOT_H
+
+/** @file
+ *
+ * iPXE sanboot API
+ *
+ * The sanboot API provides methods for hooking, unhooking,
+ * describing, and booting from SAN devices.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <ipxe/api.h>
+#include <ipxe/refcnt.h>
+#include <ipxe/list.h>
+#include <ipxe/uri.h>
+#include <ipxe/retry.h>
+#include <ipxe/process.h>
+#include <ipxe/blockdev.h>
+#include <ipxe/acpi.h>
+#include <config/sanboot.h>
+
+/** A SAN path */
+struct san_path {
+       /** Containing SAN device */
+       struct san_device *sandev;
+       /** Path index */
+       unsigned int index;
+       /** SAN device URI */
+       struct uri *uri;
+       /** List of open/closed paths */
+       struct list_head list;
+
+       /** Underlying block device interface */
+       struct interface block;
+       /** Process */
+       struct process process;
+       /** Path status */
+       int path_rc;
+
+       /** ACPI descriptor (if applicable) */
+       struct acpi_descriptor *desc;
+};
+
+/** A SAN device */
+struct san_device {
+       /** Reference count */
+       struct refcnt refcnt;
+       /** List of SAN devices */
+       struct list_head list;
+
+       /** Drive number */
+       unsigned int drive;
+       /** Flags */
+       unsigned int flags;
+
+       /** Command interface */
+       struct interface command;
+       /** Command timeout timer */
+       struct retry_timer timer;
+       /** Command status */
+       int command_rc;
+
+       /** Raw block device capacity */
+       struct block_device_capacity capacity;
+       /** Block size shift
+        *
+        * To allow for emulation of CD-ROM access, this represents
+        * the left-shift required to translate from exposed logical
+        * I/O blocks to underlying blocks.
+        */
+       unsigned int blksize_shift;
+       /** Drive is a CD-ROM */
+       int is_cdrom;
+
+       /** Driver private data */
+       void *priv;
+
+       /** Number of paths */
+       unsigned int paths;
+       /** Current active path */
+       struct san_path *active;
+       /** List of opened SAN paths */
+       struct list_head opened;
+       /** List of closed SAN paths */
+       struct list_head closed;
+       /** SAN paths */
+       struct san_path path[0];
+
+    unsigned int exdrive;
+    int int13_command;
+    void *x86_regptr;
+    uint8_t boot_catalog_sector[2048];
+};
+
+/** SAN device flags */
+enum san_device_flags {
+       /** Device should not be included in description tables */
+       SAN_NO_DESCRIBE = 0x0001,
+};
+
+/**
+ * Calculate static inline sanboot API function name
+ *
+ * @v _prefix          Subsystem prefix
+ * @v _api_func                API function
+ * @ret _subsys_func   Subsystem API function
+ */
+#define SANBOOT_INLINE( _subsys, _api_func ) \
+       SINGLE_API_INLINE ( SANBOOT_PREFIX_ ## _subsys, _api_func )
+
+/**
+ * Provide a sanboot API implementation
+ *
+ * @v _prefix          Subsystem prefix
+ * @v _api_func                API function
+ * @v _func            Implementing function
+ */
+#define PROVIDE_SANBOOT( _subsys, _api_func, _func ) \
+       PROVIDE_SINGLE_API ( SANBOOT_PREFIX_ ## _subsys, _api_func, _func )
+
+/**
+ * Provide a static inline sanboot API implementation
+ *
+ * @v _prefix          Subsystem prefix
+ * @v _api_func                API function
+ */
+#define PROVIDE_SANBOOT_INLINE( _subsys, _api_func ) \
+       PROVIDE_SINGLE_API_INLINE ( SANBOOT_PREFIX_ ## _subsys, _api_func )
+
+/* Include all architecture-independent sanboot API headers */
+#include <ipxe/null_sanboot.h>
+#include <ipxe/dummy_sanboot.h>
+#include <ipxe/efi/efi_block.h>
+
+/* Include all architecture-dependent sanboot API headers */
+#include <bits/sanboot.h>
+
+/**
+ * Hook SAN device
+ *
+ * @v drive            Drive number
+ * @v uris             List of URIs
+ * @v count            Number of URIs
+ * @v flags            Flags
+ * @ret drive          Drive number, or negative error
+ */
+int san_hook ( unsigned int drive, struct uri **uris, unsigned int count,
+              unsigned int flags );
+
+/**
+ * Unhook SAN device
+ *
+ * @v drive            Drive number
+ */
+void san_unhook ( unsigned int drive );
+
+/**
+ * Attempt to boot from a SAN device
+ *
+ * @v drive            Drive number
+ * @v filename         Filename (or NULL to use default)
+ * @ret rc             Return status code
+ */
+int san_boot ( unsigned int drive, const char *filename );
+
+/**
+ * Describe SAN devices for SAN-booted operating system
+ *
+ * @ret rc             Return status code
+ */
+int san_describe ( void );
+
+extern struct list_head san_devices;
+
+/** Iterate over all SAN devices */
+#define for_each_sandev( sandev ) \
+       list_for_each_entry ( (sandev), &san_devices, list )
+
+/** There exist some SAN devices
+ *
+ * @ret existence      Existence of SAN devices
+ */
+static inline int have_sandevs ( void ) {
+       return ( ! list_empty ( &san_devices ) );
+}
+
+/**
+ * Get reference to SAN device
+ *
+ * @v sandev           SAN device
+ * @ret sandev         SAN device
+ */
+static inline __attribute__ (( always_inline )) struct san_device *
+sandev_get ( struct san_device *sandev ) {
+       ref_get ( &sandev->refcnt );
+       return sandev;
+}
+
+/**
+ * Drop reference to SAN device
+ *
+ * @v sandev           SAN device
+ */
+static inline __attribute__ (( always_inline )) void
+sandev_put ( struct san_device *sandev ) {
+       ref_put ( &sandev->refcnt );
+}
+
+/**
+ * Calculate SAN device block size
+ *
+ * @v sandev           SAN device
+ * @ret blksize                Sector size
+ */
+static inline size_t sandev_blksize ( struct san_device *sandev ) {
+       return ( sandev->capacity.blksize << sandev->blksize_shift );
+}
+
+/**
+ * Calculate SAN device capacity
+ *
+ * @v sandev           SAN device
+ * @ret blocks         Number of blocks
+ */
+static inline uint64_t sandev_capacity ( struct san_device *sandev ) {
+       return ( sandev->capacity.blocks >> sandev->blksize_shift );
+}
+
+/**
+ * Check if SAN device needs to be reopened
+ *
+ * @v sandev           SAN device
+ * @ret needs_reopen   SAN device needs to be reopened
+ */
+static inline int sandev_needs_reopen ( struct san_device *sandev ) {
+       return ( sandev->active == NULL );
+}
+
+extern struct san_device * sandev_find ( unsigned int drive );
+extern int sandev_reopen ( struct san_device *sandev );
+extern int sandev_reset ( struct san_device *sandev );
+extern int sandev_read ( struct san_device *sandev, uint64_t lba,
+                        unsigned int count, userptr_t buffer );
+extern int sandev_write ( struct san_device *sandev, uint64_t lba,
+                         unsigned int count, userptr_t buffer );
+extern struct san_device * alloc_sandev ( struct uri **uris, unsigned int count,
+                                         size_t priv_size );
+extern int register_sandev ( struct san_device *sandev, unsigned int drive,
+                            unsigned int flags );
+extern void unregister_sandev ( struct san_device *sandev );
+extern unsigned int san_default_drive ( void );
+
+#endif /* _IPXE_SANBOOT_H */
diff --git a/IPXE/ipxe-3fe683e/src/include/ventoy.h b/IPXE/ipxe-3fe683e/src/include/ventoy.h
new file mode 100644 (file)
index 0000000..07b8d3c
--- /dev/null
@@ -0,0 +1,215 @@
+
+#ifndef __VENTOY_VDISK_H__
+#define __VENTOY_VDISK_H__
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#define grub_uint64_t  uint64_t
+#define grub_uint32_t  uint32_t
+#define grub_uint16_t  uint16_t
+#define grub_uint8_t   uint8_t
+
+#define COMPILE_ASSERT(expr)  extern char __compile_assert[(expr) ? 1 : -1]
+
+#define VENTOY_GUID { 0x77772020, 0x2e77, 0x6576, { 0x6e, 0x74, 0x6f, 0x79, 0x2e, 0x6e, 0x65, 0x74 }}
+
+#pragma pack(1)
+
+typedef struct ventoy_guid
+{
+    grub_uint32_t   data1;
+    grub_uint16_t   data2;
+    grub_uint16_t   data3;
+    grub_uint8_t    data4[8];
+}ventoy_guid;
+
+typedef struct ventoy_image_disk_region
+{
+    grub_uint32_t   image_sector_count; /* image sectors contained in this region */
+    grub_uint32_t   image_start_sector; /* image sector start */
+    grub_uint64_t   disk_start_sector;  /* disk sector start */
+}ventoy_image_disk_region;
+
+typedef struct ventoy_image_location
+{
+    ventoy_guid  guid;
+    
+    /* image sector size, currently this value is always 2048 */
+    grub_uint32_t   image_sector_size;
+
+    /* disk sector size, normally the value is 512 */
+    grub_uint32_t   disk_sector_size;
+
+    grub_uint32_t   region_count;
+
+    /*
+     * disk region data
+     * If the image file has more than one fragments in disk, 
+     * there will be more than one region data here.
+     *
+     */
+    ventoy_image_disk_region regions[1];
+
+    /* ventoy_image_disk_region regions[2~region_count-1] */
+}ventoy_image_location;
+
+typedef struct ventoy_os_param
+{
+    ventoy_guid    guid;                  // VENTOY_GUID
+    grub_uint8_t   chksum;                // checksum
+
+    grub_uint8_t   vtoy_disk_guid[16];
+    grub_uint64_t  vtoy_disk_size;       // disk size in bytes
+    grub_uint16_t  vtoy_disk_part_id;    // begin with 1
+    grub_uint16_t  vtoy_disk_part_type;  // 0:exfat   1:ntfs  other: reserved
+    char           vtoy_img_path[384];   // It seems to be enough, utf-8 format
+    grub_uint64_t  vtoy_img_size;        // image file size in bytes
+
+    /* 
+     * Ventoy will write a copy of ventoy_image_location data into runtime memory
+     * this is the physically address and length of that memory.
+     * Address 0 means no such data exist.
+     * Address will be aligned by 4KB.
+     *
+     */
+    grub_uint64_t  vtoy_img_location_addr;
+    grub_uint32_t  vtoy_img_location_len;
+
+    grub_uint64_t  vtoy_reserved[4];     // Internal use by ventoy
+
+    grub_uint8_t   reserved[31];
+}ventoy_os_param;
+
+#pragma pack()
+
+// compile assert to check that size of ventoy_os_param must be 512
+COMPILE_ASSERT(sizeof(ventoy_os_param) == 512);
+
+
+
+
+
+
+
+#pragma pack(4)
+
+typedef struct ventoy_chain_head
+{
+    ventoy_os_param os_param;
+
+    grub_uint32_t disk_drive;
+    grub_uint32_t drive_map;    
+    grub_uint32_t disk_sector_size;
+
+    grub_uint64_t real_img_size_in_bytes;
+    grub_uint64_t virt_img_size_in_bytes;
+    grub_uint32_t boot_catalog;
+    grub_uint8_t  boot_catalog_sector[2048];
+    
+    grub_uint32_t img_chunk_offset;
+    grub_uint32_t img_chunk_num;
+
+    grub_uint32_t override_chunk_offset;
+    grub_uint32_t override_chunk_num;
+
+    grub_uint32_t virt_chunk_offset;
+    grub_uint32_t virt_chunk_num;
+}ventoy_chain_head;
+
+
+typedef struct ventoy_img_chunk
+{
+    grub_uint32_t img_start_sector; //2KB
+    grub_uint32_t img_end_sector;
+
+    grub_uint64_t disk_start_sector; // in disk_sector_size
+    grub_uint64_t disk_end_sector;
+}ventoy_img_chunk;
+
+
+typedef struct ventoy_override_chunk
+{
+    grub_uint64_t img_offset;
+    grub_uint32_t override_size;
+    grub_uint8_t  override_data[512];
+}ventoy_override_chunk;
+
+typedef struct ventoy_virt_chunk
+{
+    grub_uint32_t mem_sector_start;
+    grub_uint32_t mem_sector_end;
+    grub_uint32_t mem_sector_offset;
+    grub_uint32_t remap_sector_start;
+    grub_uint32_t remap_sector_end;
+    grub_uint32_t org_sector_start;
+}ventoy_virt_chunk;
+
+
+#pragma pack()
+
+
+#define ventoy_debug_pause()    \
+{\
+    printf("\nPress Ctrl+C to continue......");\
+    sleep(3600);\
+    printf("\n");\
+}
+
+typedef struct ventoy_sector_flag
+{
+    uint8_t flag; // 0:init   1:mem  2:remap
+    uint64_t remap_lba;    
+}ventoy_sector_flag;
+
+#define VENTOY_BIOS_FAKE_DRIVE  0xFE
+
+extern char *g_cmdline_copy;
+extern void *g_initrd_addr;
+extern size_t g_initrd_len;
+extern uint32_t g_disk_sector_size;
+unsigned int ventoy_int13_hook (ventoy_chain_head *chain);
+int ventoy_int13_boot ( unsigned int drive, void *imginfo, const char *cmdline);
+void * ventoy_get_runtime_addr(void);
+int ventoy_boot_vdisk(void *data);
+
+
+uint32_t CalculateCrc32
+(
+    const void       *Buffer,
+    uint32_t          Length,
+    uint32_t          InitValue
+);
+
+struct smbios3_entry {
+       
+       uint8_t signature[5];
+
+    /** Checksum */
+       uint8_t checksum;
+
+    /** Length */
+       uint8_t len;
+
+    /** Major version */
+       uint8_t major;
+
+    /** Minor version */
+       uint8_t minor;
+    
+       uint8_t docrev;
+    
+       uint8_t revision;
+    
+       uint8_t reserved;
+
+    uint32_t maxsize;
+
+    uint64_t address;
+} __attribute__ (( packed ));
+
+
+//#undef DBGLVL
+//#define DBGLVL 7
+
+#endif /* __VENTOY_VDISK_H__ */
+
diff --git a/IPXE/ipxe-3fe683e/src/interface/efi/efi_pci.c b/IPXE/ipxe-3fe683e/src/interface/efi/efi_pci.c
new file mode 100644 (file)
index 0000000..6726ef3
--- /dev/null
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2008 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#if 0
+
+#include <stdlib.h>
+#include <errno.h>
+#include <ipxe/pci.h>
+#include <ipxe/efi/efi.h>
+#include <ipxe/efi/efi_pci.h>
+#include <ipxe/efi/efi_driver.h>
+#include <ipxe/efi/Protocol/PciIo.h>
+#include <ipxe/efi/Protocol/PciRootBridgeIo.h>
+
+/** @file
+ *
+ * iPXE PCI I/O API for EFI
+ *
+ */
+
+/* Disambiguate the various error causes */
+#define EINFO_EEFI_PCI                                                 \
+       __einfo_uniqify ( EINFO_EPLATFORM, 0x01,                        \
+                         "Could not open PCI I/O protocol" )
+#define EINFO_EEFI_PCI_NOT_PCI                                         \
+       __einfo_platformify ( EINFO_EEFI_PCI, EFI_UNSUPPORTED,          \
+                             "Not a PCI device" )
+#define EEFI_PCI_NOT_PCI __einfo_error ( EINFO_EEFI_PCI_NOT_PCI )
+#define EINFO_EEFI_PCI_IN_USE                                          \
+       __einfo_platformify ( EINFO_EEFI_PCI, EFI_ACCESS_DENIED,        \
+                             "PCI device already has a driver" )
+#define EEFI_PCI_IN_USE __einfo_error ( EINFO_EEFI_PCI_IN_USE )
+#define EEFI_PCI( efirc )                                              \
+       EPLATFORM ( EINFO_EEFI_PCI, efirc,                              \
+                   EEFI_PCI_NOT_PCI, EEFI_PCI_IN_USE )
+
+/******************************************************************************
+ *
+ * iPXE PCI API
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Locate EFI PCI root bridge I/O protocol
+ *
+ * @v pci              PCI device
+ * @ret handle         EFI PCI root bridge handle
+ * @ret root           EFI PCI root bridge I/O protocol, or NULL if not found
+ * @ret rc             Return status code
+ */
+static int efipci_root ( struct pci_device *pci, EFI_HANDLE *handle,
+                        EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL **root ) {
+       EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+       EFI_HANDLE *handles;
+       UINTN num_handles;
+       union {
+               void *interface;
+               EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root;
+       } u;
+       EFI_STATUS efirc;
+       UINTN i;
+       int rc;
+
+       /* Enumerate all handles */
+       if ( ( efirc = bs->LocateHandleBuffer ( ByProtocol,
+                       &efi_pci_root_bridge_io_protocol_guid,
+                       NULL, &num_handles, &handles ) ) != 0 ) {
+               rc = -EEFI ( efirc );
+               DBGC ( pci, "EFIPCI " PCI_FMT " cannot locate root bridges: "
+                      "%s\n", PCI_ARGS ( pci ), strerror ( rc ) );
+               goto err_locate;
+       }
+
+       /* Look for matching root bridge I/O protocol */
+       for ( i = 0 ; i < num_handles ; i++ ) {
+               *handle = handles[i];
+               if ( ( efirc = bs->OpenProtocol ( *handle,
+                               &efi_pci_root_bridge_io_protocol_guid,
+                               &u.interface, efi_image_handle, *handle,
+                               EFI_OPEN_PROTOCOL_GET_PROTOCOL ) ) != 0 ) {
+                       rc = -EEFI ( efirc );
+                       DBGC ( pci, "EFIPCI " PCI_FMT " cannot open %s: %s\n",
+                              PCI_ARGS ( pci ), efi_handle_name ( *handle ),
+                              strerror ( rc ) );
+                       continue;
+               }
+               if ( u.root->SegmentNumber == PCI_SEG ( pci->busdevfn ) ) {
+                       *root = u.root;
+                       bs->FreePool ( handles );
+                       return 0;
+               }
+               bs->CloseProtocol ( *handle,
+                                   &efi_pci_root_bridge_io_protocol_guid,
+                                   efi_image_handle, *handle );
+       }
+       DBGC ( pci, "EFIPCI " PCI_FMT " found no root bridge\n",
+              PCI_ARGS ( pci ) );
+       rc = -ENOENT;
+
+       bs->FreePool ( handles );
+ err_locate:
+       return rc;
+}
+
+/**
+ * Calculate EFI PCI configuration space address
+ *
+ * @v pci              PCI device
+ * @v location         Encoded offset and width
+ * @ret address                EFI PCI address
+ */
+static unsigned long efipci_address ( struct pci_device *pci,
+                                     unsigned long location ) {
+
+       return EFI_PCI_ADDRESS ( PCI_BUS ( pci->busdevfn ),
+                                PCI_SLOT ( pci->busdevfn ),
+                                PCI_FUNC ( pci->busdevfn ),
+                                EFIPCI_OFFSET ( location ) );
+}
+
+/**
+ * Read from PCI configuration space
+ *
+ * @v pci              PCI device
+ * @v location         Encoded offset and width
+ * @ret value          Value
+ * @ret rc             Return status code
+ */
+int efipci_read ( struct pci_device *pci, unsigned long location,
+                 void *value ) {
+       EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+       EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root;
+       EFI_HANDLE handle;
+       EFI_STATUS efirc;
+       int rc;
+
+       /* Identify root bridge */
+       if ( ( rc = efipci_root ( pci, &handle, &root ) ) != 0 )
+               goto err_root;
+
+       /* Read from configuration space */
+       if ( ( efirc = root->Pci.Read ( root, EFIPCI_WIDTH ( location ),
+                                       efipci_address ( pci, location ), 1,
+                                       value ) ) != 0 ) {
+               rc = -EEFI ( efirc );
+               DBGC ( pci, "EFIPCI " PCI_FMT " config read from offset %02lx "
+                      "failed: %s\n", PCI_ARGS ( pci ),
+                      EFIPCI_OFFSET ( location ), strerror ( rc ) );
+               goto err_read;
+       }
+
+ err_read:
+       bs->CloseProtocol ( handle, &efi_pci_root_bridge_io_protocol_guid,
+                           efi_image_handle, handle );
+ err_root:
+       return rc;
+}
+
+/**
+ * Write to PCI configuration space
+ *
+ * @v pci              PCI device
+ * @v location         Encoded offset and width
+ * @v value            Value
+ * @ret rc             Return status code
+ */
+int efipci_write ( struct pci_device *pci, unsigned long location,
+                  unsigned long value ) {
+       EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+       EFI_PCI_ROOT_BRIDGE_IO_PROTOCOL *root;
+       EFI_HANDLE handle;
+       EFI_STATUS efirc;
+       int rc;
+
+       /* Identify root bridge */
+       if ( ( rc = efipci_root ( pci, &handle, &root ) ) != 0 )
+               goto err_root;
+
+       /* Read from configuration space */
+       if ( ( efirc = root->Pci.Write ( root, EFIPCI_WIDTH ( location ),
+                                        efipci_address ( pci, location ), 1,
+                                        &value ) ) != 0 ) {
+               rc = -EEFI ( efirc );
+               DBGC ( pci, "EFIPCI " PCI_FMT " config write to offset %02lx "
+                      "failed: %s\n", PCI_ARGS ( pci ),
+                      EFIPCI_OFFSET ( location ), strerror ( rc ) );
+               goto err_write;
+       }
+
+ err_write:
+       bs->CloseProtocol ( handle, &efi_pci_root_bridge_io_protocol_guid,
+                           efi_image_handle, handle );
+ err_root:
+       return rc;
+}
+
+PROVIDE_PCIAPI_INLINE ( efi, pci_num_bus );
+PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_byte );
+PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_word );
+PROVIDE_PCIAPI_INLINE ( efi, pci_read_config_dword );
+PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_byte );
+PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_word );
+PROVIDE_PCIAPI_INLINE ( efi, pci_write_config_dword );
+
+/******************************************************************************
+ *
+ * EFI PCI device instantiation
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Open EFI PCI device
+ *
+ * @v device           EFI device handle
+ * @v attributes       Protocol opening attributes
+ * @v pci              PCI device to fill in
+ * @ret rc             Return status code
+ */
+int efipci_open ( EFI_HANDLE device, UINT32 attributes,
+                 struct pci_device *pci ) {
+       EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+       union {
+               EFI_PCI_IO_PROTOCOL *pci_io;
+               void *interface;
+       } pci_io;
+       UINTN pci_segment, pci_bus, pci_dev, pci_fn;
+       unsigned int busdevfn;
+       EFI_STATUS efirc;
+       int rc;
+
+       /* See if device is a PCI device */
+       if ( ( efirc = bs->OpenProtocol ( device, &efi_pci_io_protocol_guid,
+                                         &pci_io.interface, efi_image_handle,
+                                         device, attributes ) ) != 0 ) {
+               rc = -EEFI_PCI ( efirc );
+               DBGCP ( device, "EFIPCI %s cannot open PCI protocols: %s\n",
+                       efi_handle_name ( device ), strerror ( rc ) );
+               goto err_open_protocol;
+       }
+
+       /* Get PCI bus:dev.fn address */
+       if ( ( efirc = pci_io.pci_io->GetLocation ( pci_io.pci_io, &pci_segment,
+                                                   &pci_bus, &pci_dev,
+                                                   &pci_fn ) ) != 0 ) {
+               rc = -EEFI ( efirc );
+               DBGC ( device, "EFIPCI %s could not get PCI location: %s\n",
+                      efi_handle_name ( device ), strerror ( rc ) );
+               goto err_get_location;
+       }
+       busdevfn = PCI_BUSDEVFN ( pci_segment, pci_bus, pci_dev, pci_fn );
+       pci_init ( pci, busdevfn );
+       DBGCP ( device, "EFIPCI " PCI_FMT " is %s\n",
+               PCI_ARGS ( pci ), efi_handle_name ( device ) );
+
+       /* Try to enable I/O cycles, memory cycles, and bus mastering.
+        * Some platforms will 'helpfully' report errors if these bits
+        * can't be enabled (for example, if the card doesn't actually
+        * support I/O cycles).  Work around any such platforms by
+        * enabling bits individually and simply ignoring any errors.
+        */
+       pci_io.pci_io->Attributes ( pci_io.pci_io,
+                                   EfiPciIoAttributeOperationEnable,
+                                   EFI_PCI_IO_ATTRIBUTE_IO, NULL );
+       pci_io.pci_io->Attributes ( pci_io.pci_io,
+                                   EfiPciIoAttributeOperationEnable,
+                                   EFI_PCI_IO_ATTRIBUTE_MEMORY, NULL );
+       pci_io.pci_io->Attributes ( pci_io.pci_io,
+                                   EfiPciIoAttributeOperationEnable,
+                                   EFI_PCI_IO_ATTRIBUTE_BUS_MASTER, NULL );
+
+       /* Populate PCI device */
+       if ( ( rc = pci_read_config ( pci ) ) != 0 ) {
+               DBGC ( device, "EFIPCI " PCI_FMT " cannot read PCI "
+                      "configuration: %s\n",
+                      PCI_ARGS ( pci ), strerror ( rc ) );
+               goto err_pci_read_config;
+       }
+
+       return 0;
+
+ err_pci_read_config:
+ err_get_location:
+       bs->CloseProtocol ( device, &efi_pci_io_protocol_guid,
+                           efi_image_handle, device );
+ err_open_protocol:
+       return rc;
+}
+
+/**
+ * Close EFI PCI device
+ *
+ * @v device           EFI device handle
+ */
+void efipci_close ( EFI_HANDLE device ) {
+       EFI_BOOT_SERVICES *bs = efi_systab->BootServices;
+
+       bs->CloseProtocol ( device, &efi_pci_io_protocol_guid,
+                           efi_image_handle, device );
+}
+
+/**
+ * Get EFI PCI device information
+ *
+ * @v device           EFI device handle
+ * @v pci              PCI device to fill in
+ * @ret rc             Return status code
+ */
+int efipci_info ( EFI_HANDLE device, struct pci_device *pci ) {
+       int rc;
+
+       /* Open PCI device, if possible */
+       if ( ( rc = efipci_open ( device, EFI_OPEN_PROTOCOL_GET_PROTOCOL,
+                                 pci ) ) != 0 )
+               return rc;
+
+       /* Close PCI device */
+       efipci_close ( device );
+
+       return 0;
+}
+
+/******************************************************************************
+ *
+ * EFI PCI driver
+ *
+ ******************************************************************************
+ */
+
+/**
+ * Check to see if driver supports a device
+ *
+ * @v device           EFI device handle
+ * @ret rc             Return status code
+ */
+static int efipci_supported ( EFI_HANDLE device ) {
+       struct pci_device pci;
+       int rc;
+
+       /* Get PCI device information */
+       if ( ( rc = efipci_info ( device, &pci ) ) != 0 )
+               return rc;
+
+       /* Look for a driver */
+       if ( ( rc = pci_find_driver ( &pci ) ) != 0 ) {
+               DBGC ( device, "EFIPCI " PCI_FMT " (%04x:%04x class %06x) "
+                      "has no driver\n", PCI_ARGS ( &pci ), pci.vendor,
+                      pci.device, pci.class );
+               return rc;
+       }
+       DBGC ( device, "EFIPCI " PCI_FMT " (%04x:%04x class %06x) has driver "
+              "\"%s\"\n", PCI_ARGS ( &pci ), pci.vendor, pci.device,
+              pci.class, pci.id->name );
+
+       return 0;
+}
+
+/**
+ * Attach driver to device
+ *
+ * @v efidev           EFI device
+ * @ret rc             Return status code
+ */
+static int efipci_start ( struct efi_device *efidev ) {
+       EFI_HANDLE device = efidev->device;
+       struct pci_device *pci;
+       int rc;
+
+       /* Allocate PCI device */
+       pci = zalloc ( sizeof ( *pci ) );
+       if ( ! pci ) {
+               rc = -ENOMEM;
+               goto err_alloc;
+       }
+
+       /* Open PCI device */
+       if ( ( rc = efipci_open ( device, ( EFI_OPEN_PROTOCOL_BY_DRIVER |
+                                           EFI_OPEN_PROTOCOL_EXCLUSIVE ),
+                                 pci ) ) != 0 ) {
+               DBGC ( device, "EFIPCI %s could not open PCI device: %s\n",
+                      efi_handle_name ( device ), strerror ( rc ) );
+               DBGC_EFI_OPENERS ( device, device, &efi_pci_io_protocol_guid );
+               goto err_open;
+       }
+
+       /* Find driver */
+       if ( ( rc = pci_find_driver ( pci ) ) != 0 ) {
+               DBGC ( device, "EFIPCI " PCI_FMT " has no driver\n",
+                      PCI_ARGS ( pci ) );
+               goto err_find_driver;
+       }
+
+       /* Mark PCI device as a child of the EFI device */
+       pci->dev.parent = &efidev->dev;
+       list_add ( &pci->dev.siblings, &efidev->dev.children );
+
+       /* Probe driver */
+       if ( ( rc = pci_probe ( pci ) ) != 0 ) {
+               DBGC ( device, "EFIPCI " PCI_FMT " could not probe driver "
+                      "\"%s\": %s\n", PCI_ARGS ( pci ), pci->id->name,
+                      strerror ( rc ) );
+               goto err_probe;
+       }
+       DBGC ( device, "EFIPCI " PCI_FMT " using driver \"%s\"\n",
+              PCI_ARGS ( pci ), pci->id->name );
+
+       efidev_set_drvdata ( efidev, pci );
+       return 0;
+
+       pci_remove ( pci );
+ err_probe:
+       list_del ( &pci->dev.siblings );
+ err_find_driver:
+       efipci_close ( device );
+ err_open:
+       free ( pci );
+ err_alloc:
+       return rc;
+}
+
+/**
+ * Detach driver from device
+ *
+ * @v efidev           EFI device
+  */
+static void efipci_stop ( struct efi_device *efidev ) {
+       struct pci_device *pci = efidev_get_drvdata ( efidev );
+       EFI_HANDLE device = efidev->device;
+
+       pci_remove ( pci );
+       list_del ( &pci->dev.siblings );
+       efipci_close ( device );
+       free ( pci );
+}
+
+/** EFI PCI driver */
+struct efi_driver efipci_driver __efi_driver ( EFI_DRIVER_NORMAL ) = {
+       .name = "PCI",
+       .supported = efipci_supported,
+       .start = efipci_start,
+       .stop = efipci_stop,
+};
+
+#endif
diff --git a/IPXE/ipxe-3fe683e/src/net/tcp/iscsi.c b/IPXE/ipxe-3fe683e/src/net/tcp/iscsi.c
new file mode 100644 (file)
index 0000000..babf286
--- /dev/null
@@ -0,0 +1,2152 @@
+/*
+ * Copyright (C) 2006 Michael Brown <mbrown@fensystems.co.uk>.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of the
+ * License, or 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 Street, Fifth Floor, Boston, MA
+ * 02110-1301, USA.
+ *
+ * You can also choose to distribute this program under the terms of
+ * the Unmodified Binary Distribution Licence (as given in the file
+ * COPYING.UBDL), provided that you have satisfied its requirements.
+ */
+
+FILE_LICENCE ( GPL2_OR_LATER_OR_UBDL );
+
+#include <stddef.h>
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <ctype.h>
+#include <errno.h>
+#include <assert.h>
+#include <byteswap.h>
+#include <ipxe/vsprintf.h>
+#include <ipxe/socket.h>
+#include <ipxe/iobuf.h>
+#include <ipxe/uri.h>
+#include <ipxe/xfer.h>
+#include <ipxe/open.h>
+#include <ipxe/scsi.h>
+#include <ipxe/process.h>
+#include <ipxe/uaccess.h>
+#include <ipxe/tcpip.h>
+#include <ipxe/settings.h>
+#include <ipxe/features.h>
+#include <ipxe/base16.h>
+#include <ipxe/base64.h>
+#include <ipxe/ibft.h>
+#include <ipxe/iscsi.h>
+
+/** @file
+ *
+ * iSCSI protocol
+ *
+ */
+
+FEATURE ( FEATURE_PROTOCOL, "iSCSI", DHCP_EB_FEATURE_ISCSI, 1 );
+
+/* Disambiguate the various error causes */
+#define EACCES_INCORRECT_TARGET_USERNAME \
+       __einfo_error ( EINFO_EACCES_INCORRECT_TARGET_USERNAME )
+#define EINFO_EACCES_INCORRECT_TARGET_USERNAME \
+       __einfo_uniqify ( EINFO_EACCES, 0x01, "Incorrect target username" )
+#define EACCES_INCORRECT_TARGET_PASSWORD \
+       __einfo_error ( EINFO_EACCES_INCORRECT_TARGET_PASSWORD )
+#define EINFO_EACCES_INCORRECT_TARGET_PASSWORD \
+       __einfo_uniqify ( EINFO_EACCES, 0x02, "Incorrect target password" )
+#define EINVAL_ROOT_PATH_TOO_SHORT \
+       __einfo_error ( EINFO_EINVAL_ROOT_PATH_TOO_SHORT )
+#define EINFO_EINVAL_ROOT_PATH_TOO_SHORT \
+       __einfo_uniqify ( EINFO_EINVAL, 0x01, "Root path too short" )
+#define EINVAL_BAD_CREDENTIAL_MIX \
+       __einfo_error ( EINFO_EINVAL_BAD_CREDENTIAL_MIX )
+#define EINFO_EINVAL_BAD_CREDENTIAL_MIX \
+       __einfo_uniqify ( EINFO_EINVAL, 0x02, "Bad credential mix" )
+#define EINVAL_NO_ROOT_PATH \
+       __einfo_error ( EINFO_EINVAL_NO_ROOT_PATH )
+#define EINFO_EINVAL_NO_ROOT_PATH \
+       __einfo_uniqify ( EINFO_EINVAL, 0x03, "No root path" )
+#define EINVAL_NO_TARGET_IQN \
+       __einfo_error ( EINFO_EINVAL_NO_TARGET_IQN )
+#define EINFO_EINVAL_NO_TARGET_IQN \
+       __einfo_uniqify ( EINFO_EINVAL, 0x04, "No target IQN" )
+#define EINVAL_NO_INITIATOR_IQN \
+       __einfo_error ( EINFO_EINVAL_NO_INITIATOR_IQN )
+#define EINFO_EINVAL_NO_INITIATOR_IQN \
+       __einfo_uniqify ( EINFO_EINVAL, 0x05, "No initiator IQN" )
+#define EIO_TARGET_UNAVAILABLE \
+       __einfo_error ( EINFO_EIO_TARGET_UNAVAILABLE )
+#define EINFO_EIO_TARGET_UNAVAILABLE \
+       __einfo_uniqify ( EINFO_EIO, 0x01, "Target not currently operational" )
+#define EIO_TARGET_NO_RESOURCES \
+       __einfo_error ( EINFO_EIO_TARGET_NO_RESOURCES )
+#define EINFO_EIO_TARGET_NO_RESOURCES \
+       __einfo_uniqify ( EINFO_EIO, 0x02, "Target out of resources" )
+#define ENOTSUP_INITIATOR_STATUS \
+       __einfo_error ( EINFO_ENOTSUP_INITIATOR_STATUS )
+#define EINFO_ENOTSUP_INITIATOR_STATUS \
+       __einfo_uniqify ( EINFO_ENOTSUP, 0x01, "Unsupported initiator status" )
+#define ENOTSUP_OPCODE \
+       __einfo_error ( EINFO_ENOTSUP_OPCODE )
+#define EINFO_ENOTSUP_OPCODE \
+       __einfo_uniqify ( EINFO_ENOTSUP, 0x02, "Unsupported opcode" )
+#define ENOTSUP_DISCOVERY \
+       __einfo_error ( EINFO_ENOTSUP_DISCOVERY )
+#define EINFO_ENOTSUP_DISCOVERY \
+       __einfo_uniqify ( EINFO_ENOTSUP, 0x03, "Discovery not supported" )
+#define ENOTSUP_TARGET_STATUS \
+       __einfo_error ( EINFO_ENOTSUP_TARGET_STATUS )
+#define EINFO_ENOTSUP_TARGET_STATUS \
+       __einfo_uniqify ( EINFO_ENOTSUP, 0x04, "Unsupported target status" )
+#define EPERM_INITIATOR_AUTHENTICATION \
+       __einfo_error ( EINFO_EPERM_INITIATOR_AUTHENTICATION )
+#define EINFO_EPERM_INITIATOR_AUTHENTICATION \
+       __einfo_uniqify ( EINFO_EPERM, 0x01, "Initiator authentication failed" )
+#define EPERM_INITIATOR_AUTHORISATION \
+       __einfo_error ( EINFO_EPERM_INITIATOR_AUTHORISATION )
+#define EINFO_EPERM_INITIATOR_AUTHORISATION \
+       __einfo_uniqify ( EINFO_EPERM, 0x02, "Initiator not authorised" )
+#define EPROTO_INVALID_CHAP_ALGORITHM \
+       __einfo_error ( EINFO_EPROTO_INVALID_CHAP_ALGORITHM )
+#define EINFO_EPROTO_INVALID_CHAP_ALGORITHM \
+       __einfo_uniqify ( EINFO_EPROTO, 0x01, "Invalid CHAP algorithm" )
+#define EPROTO_INVALID_CHAP_IDENTIFIER \
+       __einfo_error ( EINFO_EPROTO_INVALID_CHAP_IDENTIFIER )
+#define EINFO_EPROTO_INVALID_CHAP_IDENTIFIER \
+       __einfo_uniqify ( EINFO_EPROTO, 0x02, "Invalid CHAP identifier" )
+#define EPROTO_INVALID_LARGE_BINARY \
+       __einfo_error ( EINFO_EPROTO_INVALID_LARGE_BINARY )
+#define EINFO_EPROTO_INVALID_LARGE_BINARY \
+       __einfo_uniqify ( EINFO_EPROTO, 0x03, "Invalid large binary value" )
+#define EPROTO_INVALID_CHAP_RESPONSE \
+       __einfo_error ( EINFO_EPROTO_INVALID_CHAP_RESPONSE )
+#define EINFO_EPROTO_INVALID_CHAP_RESPONSE \
+       __einfo_uniqify ( EINFO_EPROTO, 0x04, "Invalid CHAP response" )
+#define EPROTO_INVALID_KEY_VALUE_PAIR \
+       __einfo_error ( EINFO_EPROTO_INVALID_KEY_VALUE_PAIR )
+#define EINFO_EPROTO_INVALID_KEY_VALUE_PAIR \
+       __einfo_uniqify ( EINFO_EPROTO, 0x05, "Invalid key/value pair" )
+#define EPROTO_VALUE_REJECTED \
+       __einfo_error ( EINFO_EPROTO_VALUE_REJECTED )
+#define EINFO_EPROTO_VALUE_REJECTED                                    \
+       __einfo_uniqify ( EINFO_EPROTO, 0x06, "Parameter rejected" )
+
+static void iscsi_start_tx ( struct iscsi_session *iscsi );
+static void iscsi_start_login ( struct iscsi_session *iscsi );
+static void iscsi_start_data_out ( struct iscsi_session *iscsi,
+                                  unsigned int datasn );
+
+/**
+ * Finish receiving PDU data into buffer
+ *
+ * @v iscsi            iSCSI session
+ */
+static void iscsi_rx_buffered_data_done ( struct iscsi_session *iscsi ) {
+       free ( iscsi->rx_buffer );
+       iscsi->rx_buffer = NULL;
+}
+
+/**
+ * Receive PDU data into buffer
+ *
+ * @v iscsi            iSCSI session
+ * @v data             Data to receive
+ * @v len              Length of data
+ * @ret rc             Return status code
+ *
+ * This can be used when the RX PDU type handler wishes to buffer up
+ * all received data and process the PDU as a single unit.  The caller
+ * is repsonsible for calling iscsi_rx_buffered_data_done() after
+ * processing the data.
+ */
+static int iscsi_rx_buffered_data ( struct iscsi_session *iscsi,
+                                   const void *data, size_t len ) {
+
+       /* Allocate buffer on first call */
+       if ( ! iscsi->rx_buffer ) {
+               iscsi->rx_buffer = malloc ( iscsi->rx_len );
+               if ( ! iscsi->rx_buffer )
+                       return -ENOMEM;
+       }
+
+       /* Copy data to buffer */
+       assert ( ( iscsi->rx_offset + len ) <= iscsi->rx_len );
+       memcpy ( ( iscsi->rx_buffer + iscsi->rx_offset ), data, len );
+
+       return 0;
+}
+
+/**
+ * Free iSCSI session
+ *
+ * @v refcnt           Reference counter
+ */
+static void iscsi_free ( struct refcnt *refcnt ) {
+       struct iscsi_session *iscsi =
+               container_of ( refcnt, struct iscsi_session, refcnt );
+
+       free ( iscsi->initiator_iqn );
+       free ( iscsi->target_address );
+       free ( iscsi->target_iqn );
+       free ( iscsi->initiator_username );
+       free ( iscsi->initiator_password );
+       free ( iscsi->target_username );
+       free ( iscsi->target_password );
+       chap_finish ( &iscsi->chap );
+       iscsi_rx_buffered_data_done ( iscsi );
+       free ( iscsi->command );
+       free ( iscsi );
+}
+
+/**
+ * Shut down iSCSI interface
+ *
+ * @v iscsi            iSCSI session
+ * @v rc               Reason for close
+ */
+static void iscsi_close ( struct iscsi_session *iscsi, int rc ) {
+
+       /* A TCP graceful close is still an error from our point of view */
+       if ( rc == 0 )
+               rc = -ECONNRESET;
+
+       DBGC ( iscsi, "iSCSI %p closed: %s\n", iscsi, strerror ( rc ) );
+
+       /* Stop transmission process */
+       process_del ( &iscsi->process );
+
+       /* Shut down interfaces */
+       intfs_shutdown ( rc, &iscsi->socket, &iscsi->control, &iscsi->data,
+                        NULL );
+}
+
+/**
+ * Assign new iSCSI initiator task tag
+ *
+ * @v iscsi            iSCSI session
+ */
+static void iscsi_new_itt ( struct iscsi_session *iscsi ) {
+       static uint16_t itt_idx;
+
+       iscsi->itt = ( ISCSI_TAG_MAGIC | (++itt_idx) );
+}
+
+/**
+ * Open iSCSI transport-layer connection
+ *
+ * @v iscsi            iSCSI session
+ * @ret rc             Return status code
+ */
+static int iscsi_open_connection ( struct iscsi_session *iscsi ) {
+       struct sockaddr_tcpip target;
+       int rc;
+
+       assert ( iscsi->tx_state == ISCSI_TX_IDLE );
+       assert ( iscsi->rx_state == ISCSI_RX_BHS );
+       assert ( iscsi->rx_offset == 0 );
+
+       /* Open socket */
+       memset ( &target, 0, sizeof ( target ) );
+       target.st_port = htons ( iscsi->target_port );
+       if ( ( rc = xfer_open_named_socket ( &iscsi->socket, SOCK_STREAM,
+                                            ( struct sockaddr * ) &target,
+                                            iscsi->target_address,
+                                            NULL ) ) != 0 ) {
+               DBGC ( iscsi, "iSCSI %p could not open socket: %s\n",
+                      iscsi, strerror ( rc ) );
+               return rc;
+       }
+
+       /* Enter security negotiation phase */
+       iscsi->status = ( ISCSI_STATUS_SECURITY_NEGOTIATION_PHASE |
+                         ISCSI_STATUS_STRINGS_SECURITY );
+       if ( iscsi->target_username )
+               iscsi->status |= ISCSI_STATUS_AUTH_REVERSE_REQUIRED;
+
+       /* Assign new ISID */
+       iscsi->isid_iana_qual = ( random() & 0xffff );
+
+       /* Assign fresh initiator task tag */
+       iscsi_new_itt ( iscsi );
+
+       /* Initiate login */
+       iscsi_start_login ( iscsi );
+
+       return 0;
+}
+
+/**
+ * Close iSCSI transport-layer connection
+ *
+ * @v iscsi            iSCSI session
+ * @v rc               Reason for close
+ *
+ * Closes the transport-layer connection and resets the session state
+ * ready to attempt a fresh login.
+ */
+static void iscsi_close_connection ( struct iscsi_session *iscsi, int rc ) {
+
+       /* Close all data transfer interfaces */
+       intf_restart ( &iscsi->socket, rc );
+
+       /* Clear connection status */
+       iscsi->status = 0;
+
+       /* Reset TX and RX state machines */
+       iscsi->tx_state = ISCSI_TX_IDLE;
+       iscsi->rx_state = ISCSI_RX_BHS;
+       iscsi->rx_offset = 0;
+
+       /* Free any temporary dynamically allocated memory */
+       chap_finish ( &iscsi->chap );
+       iscsi_rx_buffered_data_done ( iscsi );
+}
+
+/**
+ * Mark iSCSI SCSI operation as complete
+ *
+ * @v iscsi            iSCSI session
+ * @v rc               Return status code
+ * @v rsp              SCSI response, if any
+ *
+ * Note that iscsi_scsi_done() will not close the connection, and must
+ * therefore be called only when the internal state machines are in an
+ * appropriate state, otherwise bad things may happen on the next call
+ * to iscsi_scsi_command().  The general rule is to call
+ * iscsi_scsi_done() only at the end of receiving a PDU; at this point
+ * the TX and RX engines should both be idle.
+ */
+static void iscsi_scsi_done ( struct iscsi_session *iscsi, int rc,
+                             struct scsi_rsp *rsp ) {
+       uint32_t itt = iscsi->itt;
+
+       assert ( iscsi->tx_state == ISCSI_TX_IDLE );
+
+       /* Clear command */
+       free ( iscsi->command );
+       iscsi->command = NULL;
+
+       /* Send SCSI response, if any */
+       if ( rsp )
+               scsi_response ( &iscsi->data, rsp );
+
+       /* Close SCSI command, if this is still the same command.  (It
+        * is possible that the command interface has already been
+        * closed as a result of the SCSI response we sent.)
+        */
+       if ( iscsi->itt == itt )
+               intf_restart ( &iscsi->data, rc );
+}
+
+/****************************************************************************
+ *
+ * iSCSI SCSI command issuing
+ *
+ */
+
+/**
+ * Build iSCSI SCSI command BHS
+ *
+ * @v iscsi            iSCSI session
+ *
+ * We don't currently support bidirectional commands (i.e. with both
+ * Data-In and Data-Out segments); these would require providing code
+ * to generate an AHS, and there doesn't seem to be any need for it at
+ * the moment.
+ */
+static void iscsi_start_command ( struct iscsi_session *iscsi ) {
+       struct iscsi_bhs_scsi_command *command = &iscsi->tx_bhs.scsi_command;
+
+       assert ( ! ( iscsi->command->data_in && iscsi->command->data_out ) );
+
+       /* Construct BHS and initiate transmission */
+       iscsi_start_tx ( iscsi );
+       command->opcode = ISCSI_OPCODE_SCSI_COMMAND;
+       command->flags = ( ISCSI_FLAG_FINAL |
+                          ISCSI_COMMAND_ATTR_SIMPLE );
+       if ( iscsi->command->data_in )
+               command->flags |= ISCSI_COMMAND_FLAG_READ;
+       if ( iscsi->command->data_out )
+               command->flags |= ISCSI_COMMAND_FLAG_WRITE;
+       /* lengths left as zero */
+       memcpy ( &command->lun, &iscsi->command->lun,
+                sizeof ( command->lun ) );
+       command->itt = htonl ( iscsi->itt );
+       command->exp_len = htonl ( iscsi->command->data_in_len |
+                                  iscsi->command->data_out_len );
+       command->cmdsn = htonl ( iscsi->cmdsn );
+       command->expstatsn = htonl ( iscsi->statsn + 1 );
+       memcpy ( &command->cdb, &iscsi->command->cdb, sizeof ( command->cdb ));
+       DBGC2 ( iscsi, "iSCSI %p start " SCSI_CDB_FORMAT " %s %#zx\n",
+               iscsi, SCSI_CDB_DATA ( command->cdb ),
+               ( iscsi->command->data_in ? "in" : "out" ),
+               ( iscsi->command->data_in ?
+                 iscsi->command->data_in_len :
+                 iscsi->command->data_out_len ) );
+}
+
+/**
+ * Receive data segment of an iSCSI SCSI response PDU
+ *
+ * @v iscsi            iSCSI session
+ * @v data             Received data
+ * @v len              Length of received data
+ * @v remaining                Data remaining after this data
+ * @ret rc             Return status code
+ */
+static int iscsi_rx_scsi_response ( struct iscsi_session *iscsi,
+                                   const void *data, size_t len,
+                                   size_t remaining ) {
+       struct iscsi_bhs_scsi_response *response
+               = &iscsi->rx_bhs.scsi_response;
+       struct scsi_rsp rsp;
+       uint32_t residual_count;
+       size_t data_len;
+       int rc;
+
+       /* Buffer up the PDU data */
+       if ( ( rc = iscsi_rx_buffered_data ( iscsi, data, len ) ) != 0 ) {
+               DBGC ( iscsi, "iSCSI %p could not buffer SCSI response: %s\n",
+                      iscsi, strerror ( rc ) );
+               return rc;
+       }
+       if ( remaining )
+               return 0;
+
+       /* Parse SCSI response and discard buffer */
+       memset ( &rsp, 0, sizeof ( rsp ) );
+       rsp.status = response->status;
+       residual_count = ntohl ( response->residual_count );
+       if ( response->flags & ISCSI_DATA_FLAG_OVERFLOW ) {
+               rsp.overrun = residual_count;
+       } else if ( response->flags & ISCSI_DATA_FLAG_UNDERFLOW ) {
+               rsp.overrun = -(residual_count);
+       }
+       data_len = ISCSI_DATA_LEN ( response->lengths );
+       if ( data_len ) {
+               scsi_parse_sense ( ( iscsi->rx_buffer + 2 ), ( data_len - 2 ),
+                                  &rsp.sense );
+       }
+       iscsi_rx_buffered_data_done ( iscsi );
+
+       /* Check for errors */
+       if ( response->response != ISCSI_RESPONSE_COMMAND_COMPLETE )
+               return -EIO;
+
+       /* Mark as completed */
+       iscsi_scsi_done ( iscsi, 0, &rsp );
+       return 0;
+}
+
+/**
+ * Receive data segment of an iSCSI data-in PDU
+ *
+ * @v iscsi            iSCSI session
+ * @v data             Received data
+ * @v len              Length of received data
+ * @v remaining                Data remaining after this data
+ * @ret rc             Return status code
+ */
+static int iscsi_rx_data_in ( struct iscsi_session *iscsi,
+                             const void *data, size_t len,
+                             size_t remaining ) {
+       struct iscsi_bhs_data_in *data_in = &iscsi->rx_bhs.data_in;
+       unsigned long offset;
+
+       /* Copy data to data-in buffer */
+       offset = ntohl ( data_in->offset ) + iscsi->rx_offset;
+       assert ( iscsi->command != NULL );
+       assert ( iscsi->command->data_in );
+       assert ( ( offset + len ) <= iscsi->command->data_in_len );
+       copy_to_user ( iscsi->command->data_in, offset, data, len );
+
+       /* Wait for whole SCSI response to arrive */
+       if ( remaining )
+               return 0;
+
+       /* Mark as completed if status is present */
+       if ( data_in->flags & ISCSI_DATA_FLAG_STATUS ) {
+               assert ( ( offset + len ) == iscsi->command->data_in_len );
+               assert ( data_in->flags & ISCSI_FLAG_FINAL );
+               /* iSCSI cannot return an error status via a data-in */
+               iscsi_scsi_done ( iscsi, 0, NULL );
+       }
+
+       return 0;
+}
+
+/**
+ * Receive data segment of an iSCSI R2T PDU
+ *
+ * @v iscsi            iSCSI session
+ * @v data             Received data
+ * @v len              Length of received data
+ * @v remaining                Data remaining after this data
+ * @ret rc             Return status code
+ */
+static int iscsi_rx_r2t ( struct iscsi_session *iscsi,
+                         const void *data __unused, size_t len __unused,
+                         size_t remaining __unused ) {
+       struct iscsi_bhs_r2t *r2t = &iscsi->rx_bhs.r2t;
+
+       /* Record transfer parameters and trigger first data-out */
+       iscsi->ttt = ntohl ( r2t->ttt );
+       iscsi->transfer_offset = ntohl ( r2t->offset );
+       iscsi->transfer_len = ntohl ( r2t->len );
+       iscsi_start_data_out ( iscsi, 0 );
+
+       return 0;
+}
+
+/**
+ * Build iSCSI data-out BHS
+ *
+ * @v iscsi            iSCSI session
+ * @v datasn           Data sequence number within the transfer
+ *
+ */
+static void iscsi_start_data_out ( struct iscsi_session *iscsi,
+                                  unsigned int datasn ) {
+       struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out;
+       unsigned long offset;
+       unsigned long remaining;
+       unsigned long len;
+
+       /* We always send 512-byte Data-Out PDUs; this removes the
+        * need to worry about the target's MaxRecvDataSegmentLength.
+        */
+       offset = datasn * 512;
+       remaining = iscsi->transfer_len - offset;
+       len = remaining;
+       if ( len > 512 )
+               len = 512;
+
+       /* Construct BHS and initiate transmission */
+       iscsi_start_tx ( iscsi );
+       data_out->opcode = ISCSI_OPCODE_DATA_OUT;
+       if ( len == remaining )
+               data_out->flags = ( ISCSI_FLAG_FINAL );
+       ISCSI_SET_LENGTHS ( data_out->lengths, 0, len );
+       data_out->lun = iscsi->command->lun;
+       data_out->itt = htonl ( iscsi->itt );
+       data_out->ttt = htonl ( iscsi->ttt );
+       data_out->expstatsn = htonl ( iscsi->statsn + 1 );
+       data_out->datasn = htonl ( datasn );
+       data_out->offset = htonl ( iscsi->transfer_offset + offset );
+       DBGC ( iscsi, "iSCSI %p start data out DataSN %#x len %#lx\n",
+              iscsi, datasn, len );
+}
+
+/**
+ * Complete iSCSI data-out PDU transmission
+ *
+ * @v iscsi            iSCSI session
+ *
+ */
+static void iscsi_data_out_done ( struct iscsi_session *iscsi ) {
+       struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out;
+
+       /* If we haven't reached the end of the sequence, start
+        * sending the next data-out PDU.
+        */
+       if ( ! ( data_out->flags & ISCSI_FLAG_FINAL ) )
+               iscsi_start_data_out ( iscsi, ntohl ( data_out->datasn ) + 1 );
+}
+
+/**
+ * Send iSCSI data-out data segment
+ *
+ * @v iscsi            iSCSI session
+ * @ret rc             Return status code
+ */
+static int iscsi_tx_data_out ( struct iscsi_session *iscsi ) {
+       struct iscsi_bhs_data_out *data_out = &iscsi->tx_bhs.data_out;
+       struct io_buffer *iobuf;
+       unsigned long offset;
+       size_t len;
+       size_t pad_len;
+
+       offset = ntohl ( data_out->offset );
+       len = ISCSI_DATA_LEN ( data_out->lengths );
+       pad_len = ISCSI_DATA_PAD_LEN ( data_out->lengths );
+
+       assert ( iscsi->command != NULL );
+       assert ( iscsi->command->data_out );
+       assert ( ( offset + len ) <= iscsi->command->data_out_len );
+
+       iobuf = xfer_alloc_iob ( &iscsi->socket, ( len + pad_len ) );
+       if ( ! iobuf )
+               return -ENOMEM;
+       
+       copy_from_user ( iob_put ( iobuf, len ),
+                        iscsi->command->data_out, offset, len );
+       memset ( iob_put ( iobuf, pad_len ), 0, pad_len );
+
+       return xfer_deliver_iob ( &iscsi->socket, iobuf );
+}
+
+/**
+ * Receive data segment of an iSCSI NOP-In
+ *
+ * @v iscsi            iSCSI session
+ * @v data             Received data
+ * @v len              Length of received data
+ * @v remaining                Data remaining after this data
+ * @ret rc             Return status code
+ */
+static int iscsi_rx_nop_in ( struct iscsi_session *iscsi,
+                            const void *data __unused, size_t len __unused,
+                            size_t remaining __unused ) {
+       struct iscsi_nop_in *nop_in = &iscsi->rx_bhs.nop_in;
+
+       DBGC2 ( iscsi, "iSCSI %p received NOP-In\n", iscsi );
+
+       /* We don't currently have the ability to respond to NOP-Ins
+        * sent as ping requests, but we can happily accept NOP-Ins
+        * sent merely to update CmdSN.
+        */
+       if ( nop_in->ttt == htonl ( ISCSI_TAG_RESERVED ) )
+               return 0;
+
+       /* Ignore any other NOP-Ins.  The target may eventually
+        * disconnect us for failing to respond, but this minimises
+        * unnecessary connection closures.
+        */
+       DBGC ( iscsi, "iSCSI %p received unsupported NOP-In with TTT %08x\n",
+              iscsi, ntohl ( nop_in->ttt ) );
+       return 0;
+}
+
+/****************************************************************************
+ *
+ * iSCSI login
+ *
+ */
+
+/**
+ * Build iSCSI login request strings
+ *
+ * @v iscsi            iSCSI session
+ *
+ * These are the initial set of strings sent in the first login
+ * request PDU.  We want the following settings:
+ *
+ *     HeaderDigest=None
+ *     DataDigest=None
+ *     MaxConnections=1 (irrelevant; we make only one connection anyway) [4]
+ *     InitialR2T=Yes [1]
+ *     ImmediateData=No (irrelevant; we never send immediate data) [4]
+ *     MaxRecvDataSegmentLength=8192 (default; we don't care) [3]
+ *     MaxBurstLength=262144 (default; we don't care) [3]
+ *     FirstBurstLength=65536 (irrelevant due to other settings) [5]
+ *     DefaultTime2Wait=0 [2]
+ *     DefaultTime2Retain=0 [2]
+ *     MaxOutstandingR2T=1
+ *     DataPDUInOrder=Yes
+ *     DataSequenceInOrder=Yes
+ *     ErrorRecoveryLevel=0
+ *
+ * [1] InitialR2T has an OR resolution function, so the target may
+ * force us to use it.  We therefore simplify our logic by always
+ * using it.
+ *
+ * [2] These ensure that we can safely start a new task once we have
+ * reconnected after a failure, without having to manually tidy up
+ * after the old one.
+ *
+ * [3] We are quite happy to use the RFC-defined default values for
+ * these parameters, but some targets (notably OpenSolaris)
+ * incorrectly assume a default value of zero, so we explicitly
+ * specify the default values.
+ *
+ * [4] We are quite happy to use the RFC-defined default values for
+ * these parameters, but some targets (notably a QNAP TS-639Pro) fail
+ * unless they are supplied, so we explicitly specify the default
+ * values.
+ *
+ * [5] FirstBurstLength is defined to be irrelevant since we already
+ * force InitialR2T=Yes and ImmediateData=No, but some targets
+ * (notably LIO as of kernel 4.11) fail unless it is specified, so we
+ * explicitly specify the default value.
+ */
+static int iscsi_build_login_request_strings ( struct iscsi_session *iscsi,
+                                              void *data, size_t len ) {
+       unsigned int used = 0;
+       const char *auth_method;
+
+       if ( iscsi->status & ISCSI_STATUS_STRINGS_SECURITY ) {
+               /* Default to allowing no authentication */
+               auth_method = "None";
+               /* If we have a credential to supply, permit CHAP */
+               if ( iscsi->initiator_username )
+                       auth_method = "CHAP,None";
+               /* If we have a credential to check, force CHAP */
+               if ( iscsi->target_username )
+                       auth_method = "CHAP";
+               used += ssnprintf ( data + used, len - used,
+                                   "InitiatorName=%s%c"
+                                   "TargetName=%s%c"
+                                   "SessionType=Normal%c"
+                                   "AuthMethod=%s%c",
+                                   iscsi->initiator_iqn, 0,
+                                   iscsi->target_iqn, 0, 0,
+                                   auth_method, 0 );
+       }
+
+       if ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_ALGORITHM ) {
+               used += ssnprintf ( data + used, len - used, "CHAP_A=5%c", 0 );
+       }
+       
+       if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_RESPONSE ) ) {
+               char buf[ base16_encoded_len ( iscsi->chap.response_len ) + 1 ];
+               assert ( iscsi->initiator_username != NULL );
+               base16_encode ( iscsi->chap.response, iscsi->chap.response_len,
+                               buf, sizeof ( buf ) );
+               used += ssnprintf ( data + used, len - used,
+                                   "CHAP_N=%s%cCHAP_R=0x%s%c",
+                                   iscsi->initiator_username, 0, buf, 0 );
+       }
+
+       if ( ( iscsi->status & ISCSI_STATUS_STRINGS_CHAP_CHALLENGE ) ) {
+               size_t challenge_len = ( sizeof ( iscsi->chap_challenge ) - 1 );
+               char buf[ base16_encoded_len ( challenge_len ) + 1 ];
+               base16_encode ( ( iscsi->chap_challenge + 1 ), challenge_len,
+                               buf, sizeof ( buf ) );
+               used += ssnprintf ( data + used, len - used,
+                                   "CHAP_I=%d%cCHAP_C=0x%s%c",
+                                   iscsi->chap_challenge[0], 0, buf, 0 );
+       }
+
+       if ( iscsi->status & ISCSI_STATUS_STRINGS_OPERATIONAL ) {
+               used += ssnprintf ( data + used, len - used,
+                                   "HeaderDigest=None%c"
+                                   "DataDigest=None%c"
+                                   "MaxConnections=1%c"
+                                   "InitialR2T=Yes%c"
+                                   "ImmediateData=No%c"
+                                   "MaxRecvDataSegmentLength=8192%c"
+                                   "MaxBurstLength=262144%c"
+                                   "FirstBurstLength=65536%c"
+                                   "DefaultTime2Wait=0%c"
+                                   "DefaultTime2Retain=0%c"
+                                   "MaxOutstandingR2T=1%c"
+                                   "DataPDUInOrder=Yes%c"
+                                   "DataSequenceInOrder=Yes%c"
+                                   "ErrorRecoveryLevel=0%c",
+                                   0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 );
+       }
+
+       return used;
+}
+
+/**
+ * Build iSCSI login request BHS
+ *
+ * @v iscsi            iSCSI session
+ */
+static void iscsi_start_login ( struct iscsi_session *iscsi ) {
+       struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
+       int len;
+
+       switch ( iscsi->status & ISCSI_LOGIN_CSG_MASK ) {
+       case ISCSI_LOGIN_CSG_SECURITY_NEGOTIATION:
+               DBGC ( iscsi, "iSCSI %p entering security negotiation\n",
+                      iscsi );
+               break;
+       case ISCSI_LOGIN_CSG_OPERATIONAL_NEGOTIATION:
+               DBGC ( iscsi, "iSCSI %p entering operational negotiation\n",
+                      iscsi );
+               break;
+       default:
+               assert ( 0 );
+       }
+
+       /* Construct BHS and initiate transmission */
+       iscsi_start_tx ( iscsi );
+       request->opcode = ( ISCSI_OPCODE_LOGIN_REQUEST |
+                           ISCSI_FLAG_IMMEDIATE );
+       request->flags = ( ( iscsi->status & ISCSI_STATUS_PHASE_MASK ) |
+                          ISCSI_LOGIN_FLAG_TRANSITION );
+       /* version_max and version_min left as zero */
+       len = iscsi_build_login_request_strings ( iscsi, NULL, 0 );
+       ISCSI_SET_LENGTHS ( request->lengths, 0, len );
+       request->isid_iana_en = htonl ( ISCSI_ISID_IANA |
+                                       IANA_EN_FEN_SYSTEMS );
+       request->isid_iana_qual = htons ( iscsi->isid_iana_qual );
+       /* tsih left as zero */
+       request->itt = htonl ( iscsi->itt );
+       /* cid left as zero */
+       request->cmdsn = htonl ( iscsi->cmdsn );
+       request->expstatsn = htonl ( iscsi->statsn + 1 );
+}
+
+/**
+ * Complete iSCSI login request PDU transmission
+ *
+ * @v iscsi            iSCSI session
+ *
+ */
+static void iscsi_login_request_done ( struct iscsi_session *iscsi ) {
+
+       /* Clear any "strings to send" flags */
+       iscsi->status &= ~ISCSI_STATUS_STRINGS_MASK;
+
+       /* Free any dynamically allocated storage used for login */
+       chap_finish ( &iscsi->chap );
+}
+
+/**
+ * Transmit data segment of an iSCSI login request PDU
+ *
+ * @v iscsi            iSCSI session
+ * @ret rc             Return status code
+ *
+ * For login requests, the data segment consists of the login strings.
+ */
+static int iscsi_tx_login_request ( struct iscsi_session *iscsi ) {
+       struct iscsi_bhs_login_request *request = &iscsi->tx_bhs.login_request;
+       struct io_buffer *iobuf;
+       size_t len;
+       size_t pad_len;
+
+       len = ISCSI_DATA_LEN ( request->lengths );
+       pad_len = ISCSI_DATA_PAD_LEN ( request->lengths );
+       iobuf = xfer_alloc_iob ( &iscsi->socket, ( len + pad_len ) );
+       if ( ! iobuf )
+               return -ENOMEM;
+       iob_put ( iobuf, len );
+       iscsi_build_login_request_strings ( iscsi, iobuf->data, len );
+       memset ( iob_put ( iobuf, pad_len ), 0, pad_len );
+
+       return xfer_deliver_iob ( &iscsi->socket, iobuf );
+}
+
+/**
+ * Decode large binary value
+ *
+ * @v encoded          Encoded large binary value
+ * @v raw              Raw data
+ * @v len              Length of data buffer
+ * @ret len            Length of raw data, or negative error
+ */
+static int iscsi_large_binary_decode ( const char *encoded, uint8_t *raw,
+                                      size_t len ) {
+
+       /* Check for initial '0x' or '0b' and decode as appropriate */
+       if ( *(encoded++) == '0' ) {
+               switch ( tolower ( *(encoded++) ) ) {
+               case 'x' :
+                       return base16_decode ( encoded, raw, len );
+               case 'b' :
+                       return base64_decode ( encoded, raw, len );
+               }
+       }
+
+       return -EPROTO_INVALID_LARGE_BINARY;
+}
+
+/**
+ * Handle iSCSI TargetAddress text value
+ *
+ * @v iscsi            iSCSI session
+ * @v value            TargetAddress value
+ * @ret rc             Return status code
+ */
+static int iscsi_handle_targetaddress_value ( struct iscsi_session *iscsi,
+                                             const char *value ) {
+       char *separator;
+
+       DBGC ( iscsi, "iSCSI %p will redirect to %s\n", iscsi, value );
+
+       /* Replace target address */
+       free ( iscsi->target_address );
+       iscsi->target_address = strdup ( value );
+       if ( ! iscsi->target_address )
+               return -ENOMEM;
+
+       /* Replace target port */
+       iscsi->target_port = htons ( ISCSI_PORT );
+       separator = strchr ( iscsi->target_address, ':' );
+       if ( separator ) {
+               *separator = '\0';
+               iscsi->target_port = strtoul ( ( separator + 1 ), NULL, 0 );
+       }
+
+       return 0;
+}
+
+/**
+ * Handle iSCSI AuthMethod text value
+ *
+ * @v iscsi            iSCSI session
+ * @v value            AuthMethod value
+ * @ret rc             Return status code
+ */
+static int iscsi_handle_authmethod_value ( struct iscsi_session *iscsi,
+                                          const char *value ) {
+
+       /* If server requests CHAP, send the CHAP_A string */
+       if ( strcmp ( value, "CHAP" ) == 0 ) {
+               DBGC ( iscsi, "iSCSI %p initiating CHAP authentication\n",
+                      iscsi );
+               iscsi->status |= ( ISCSI_STATUS_STRINGS_CHAP_ALGORITHM |
+                                  ISCSI_STATUS_AUTH_FORWARD_REQUIRED );
+       }
+
+       return 0;
+}
+
+/**
+ * Handle iSCSI CHAP_A text value
+ *
+ * @v iscsi            iSCSI session
+ * @v value            CHAP_A value
+ * @ret rc             Return status code
+ */
+static int iscsi_handle_chap_a_value ( struct iscsi_session *iscsi,
+                                      const char *value ) {
+
+       /* We only ever offer "5" (i.e. MD5) as an algorithm, so if
+        * the server responds with anything else it is a protocol
+        * violation.
+        */
+       if ( strcmp ( value, "5" ) != 0 ) {
+               DBGC ( iscsi, "iSCSI %p got invalid CHAP algorithm \"%s\"\n",
+                      iscsi, value );
+               return -EPROTO_INVALID_CHAP_ALGORITHM;
+       }
+
+       return 0;
+}
+
+/**
+ * Handle iSCSI CHAP_I text value
+ *
+ * @v iscsi            iSCSI session
+ * @v value            CHAP_I value
+ * @ret rc             Return status code
+ */
+static int iscsi_handle_chap_i_value ( struct iscsi_session *iscsi,
+                                      const char *value ) {
+       unsigned int identifier;
+       char *endp;
+       int rc;
+
+       /* The CHAP identifier is an integer value */
+       identifier = strtoul ( value, &endp, 0 );
+       if ( *endp != '\0' ) {
+               DBGC ( iscsi, "iSCSI %p saw invalid CHAP identifier \"%s\"\n",
+                      iscsi, value );
+               return -EPROTO_INVALID_CHAP_IDENTIFIER;
+       }
+
+       /* Prepare for CHAP with MD5 */
+       chap_finish ( &iscsi->chap );
+       if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) {
+               DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n",
+                      iscsi, strerror ( rc ) );
+               return rc;
+       }
+
+       /* Identifier and secret are the first two components of the
+        * challenge.
+        */
+       chap_set_identifier ( &iscsi->chap, identifier );
+       if ( iscsi->initiator_password ) {
+               chap_update ( &iscsi->chap, iscsi->initiator_password,
+                             strlen ( iscsi->initiator_password ) );
+       }
+
+       return 0;
+}
+
+/**
+ * Handle iSCSI CHAP_C text value
+ *
+ * @v iscsi            iSCSI session
+ * @v value            CHAP_C value
+ * @ret rc             Return status code
+ */
+static int iscsi_handle_chap_c_value ( struct iscsi_session *iscsi,
+                                      const char *value ) {
+       uint8_t buf[ strlen ( value ) ]; /* Decoding never expands data */
+       unsigned int i;
+       int len;
+       int rc;
+
+       /* Process challenge */
+       len = iscsi_large_binary_decode ( value, buf, sizeof ( buf ) );
+       if ( len < 0 ) {
+               rc = len;
+               DBGC ( iscsi, "iSCSI %p invalid CHAP challenge \"%s\": %s\n",
+                      iscsi, value, strerror ( rc ) );
+               return rc;
+       }
+       chap_update ( &iscsi->chap, buf, len );
+
+       /* Build CHAP response */
+       DBGC ( iscsi, "iSCSI %p sending CHAP response\n", iscsi );
+       chap_respond ( &iscsi->chap );
+       iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_RESPONSE;
+
+       /* Send CHAP challenge, if applicable */
+       if ( iscsi->target_username ) {
+               iscsi->status |= ISCSI_STATUS_STRINGS_CHAP_CHALLENGE;
+               /* Generate CHAP challenge data */
+               for ( i = 0 ; i < sizeof ( iscsi->chap_challenge ) ; i++ ) {
+                       iscsi->chap_challenge[i] = random();
+               }
+       }
+
+       return 0;
+}
+
+/**
+ * Handle iSCSI CHAP_N text value
+ *
+ * @v iscsi            iSCSI session
+ * @v value            CHAP_N value
+ * @ret rc             Return status code
+ */
+static int iscsi_handle_chap_n_value ( struct iscsi_session *iscsi,
+                                      const char *value ) {
+
+       /* The target username isn't actually involved at any point in
+        * the authentication process; it merely serves to identify
+        * which password the target is using to generate the CHAP
+        * response.  We unnecessarily verify that the username is as
+        * expected, in order to provide mildly helpful diagnostics if
+        * the target is supplying the wrong username/password
+        * combination.
+        */
+       if ( iscsi->target_username &&
+            ( strcmp ( iscsi->target_username, value ) != 0 ) ) {
+               DBGC ( iscsi, "iSCSI %p target username \"%s\" incorrect "
+                      "(wanted \"%s\")\n",
+                      iscsi, value, iscsi->target_username );
+               return -EACCES_INCORRECT_TARGET_USERNAME;
+       }
+
+       return 0;
+}
+
+/**
+ * Handle iSCSI CHAP_R text value
+ *
+ * @v iscsi            iSCSI session
+ * @v value            CHAP_R value
+ * @ret rc             Return status code
+ */
+static int iscsi_handle_chap_r_value ( struct iscsi_session *iscsi,
+                                      const char *value ) {
+       uint8_t buf[ strlen ( value ) ]; /* Decoding never expands data */
+       int len;
+       int rc;
+
+       /* Generate CHAP response for verification */
+       chap_finish ( &iscsi->chap );
+       if ( ( rc = chap_init ( &iscsi->chap, &md5_algorithm ) ) != 0 ) {
+               DBGC ( iscsi, "iSCSI %p could not initialise CHAP: %s\n",
+                      iscsi, strerror ( rc ) );
+               return rc;
+       }
+       chap_set_identifier ( &iscsi->chap, iscsi->chap_challenge[0] );
+       if ( iscsi->target_password ) {
+               chap_update ( &iscsi->chap, iscsi->target_password,
+                             strlen ( iscsi->target_password ) );
+       }
+       chap_update ( &iscsi->chap, &iscsi->chap_challenge[1],
+                     ( sizeof ( iscsi->chap_challenge ) - 1 ) );
+       chap_respond ( &iscsi->chap );
+
+       /* Process response */
+       len = iscsi_large_binary_decode ( value, buf, sizeof ( buf ) );
+       if ( len < 0 ) {
+               rc = len;
+               DBGC ( iscsi, "iSCSI %p invalid CHAP response \"%s\": %s\n",
+                      iscsi, value, strerror ( rc ) );
+               return rc;
+       }
+
+       /* Check CHAP response */
+       if ( len != ( int ) iscsi->chap.response_len ) {
+               DBGC ( iscsi, "iSCSI %p invalid CHAP response length\n",
+                      iscsi );
+               return -EPROTO_INVALID_CHAP_RESPONSE;
+       }
+       if ( memcmp ( buf, iscsi->chap.response, len ) != 0 ) {
+               DBGC ( iscsi, "iSCSI %p incorrect CHAP response \"%s\"\n",
+                      iscsi, value );
+               return -EACCES_INCORRECT_TARGET_PASSWORD;
+       }
+
+       /* Mark session as authenticated */
+       iscsi->status |= ISCSI_STATUS_AUTH_REVERSE_OK;
+
+       return 0;
+}
+
+/** An iSCSI text string that we want to handle */
+struct iscsi_string_type {
+       /** String key
+        *
+        * This is the portion preceding the "=" sign,
+        * e.g. "InitiatorName", "CHAP_A", etc.
+        */
+       const char *key;
+       /** Handle iSCSI string value
+        *
+        * @v iscsi             iSCSI session
+        * @v value             iSCSI string value
+        * @ret rc              Return status code
+        */
+       int ( * handle ) ( struct iscsi_session *iscsi, const char *value );
+};
+
+/** iSCSI text strings that we want to handle */
+static struct iscsi_string_type iscsi_string_types[] = {
+       { "TargetAddress", iscsi_handle_targetaddress_value },
+       { "AuthMethod", iscsi_handle_authmethod_value },
+       { "CHAP_A", iscsi_handle_chap_a_value },
+       { "CHAP_I", iscsi_handle_chap_i_value },
+       { "CHAP_C", iscsi_handle_chap_c_value },
+       { "CHAP_N", iscsi_handle_chap_n_value },
+       { "CHAP_R", iscsi_handle_chap_r_value },
+       { NULL, NULL }
+};
+
+/**
+ * Handle iSCSI string
+ *
+ * @v iscsi            iSCSI session
+ * @v string           iSCSI string (in "key=value" format)
+ * @ret rc             Return status code
+ */
+static int iscsi_handle_string ( struct iscsi_session *iscsi,
+                                const char *string ) {
+       struct iscsi_string_type *type;
+       const char *separator;
+       const char *value;
+       size_t key_len;
+       int rc;
+
+       /* Find separator */
+       separator = strchr ( string, '=' );
+       if ( ! separator ) {
+               DBGC ( iscsi, "iSCSI %p malformed string %s\n",
+                      iscsi, string );
+               return -EPROTO_INVALID_KEY_VALUE_PAIR;
+       }
+       key_len = ( separator - string );
+       value = ( separator + 1 );
+
+       /* Check for rejections.  Since we send only non-rejectable
+        * values, any rejection is a fatal protocol error.
+        */
+       if ( strcmp ( value, "Reject" ) == 0 ) {
+               DBGC ( iscsi, "iSCSI %p rejection: %s\n", iscsi, string );
+               return -EPROTO_VALUE_REJECTED;
+       }
+
+       /* Handle key/value pair */
+       for ( type = iscsi_string_types ; type->key ; type++ ) {
+               if ( strncmp ( string, type->key, key_len ) != 0 )
+                       continue;
+               DBGC ( iscsi, "iSCSI %p handling %s\n", iscsi, string );
+               if ( ( rc = type->handle ( iscsi, value ) ) != 0 ) {
+                       DBGC ( iscsi, "iSCSI %p could not handle %s: %s\n",
+                              iscsi, string, strerror ( rc ) );
+                       return rc;
+               }
+               return 0;
+       }
+       DBGC ( iscsi, "iSCSI %p ignoring %s\n", iscsi, string );
+       return 0;
+}
+
+/**
+ * Handle iSCSI strings
+ *
+ * @v iscsi            iSCSI session
+ * @v string           iSCSI string buffer
+ * @v len              Length of string buffer
+ * @ret rc             Return status code
+ */
+static int iscsi_handle_strings ( struct iscsi_session *iscsi,
+                                 const char *strings, size_t len ) {
+       size_t string_len;
+       int rc;
+
+       /* Handle each string in turn, taking care not to overrun the
+        * data buffer in case of badly-terminated data.
+        */
+       while ( 1 ) {
+               string_len = ( strnlen ( strings, len ) + 1 );
+               if ( string_len > len )
+                       break;
+               if ( ( rc = iscsi_handle_string ( iscsi, strings ) ) != 0 )
+                       return rc;
+               strings += string_len;
+               len -= string_len;
+       }
+       return 0;
+}
+
+/**
+ * Convert iSCSI response status to return status code
+ *
+ * @v status_class     iSCSI status class
+ * @v status_detail    iSCSI status detail
+ * @ret rc             Return status code
+ */
+static int iscsi_status_to_rc ( unsigned int status_class,
+                               unsigned int status_detail ) {
+       switch ( status_class ) {
+       case ISCSI_STATUS_INITIATOR_ERROR :
+               switch ( status_detail ) {
+               case ISCSI_STATUS_INITIATOR_ERROR_AUTHENTICATION :
+                       return -EPERM_INITIATOR_AUTHENTICATION;
+               case ISCSI_STATUS_INITIATOR_ERROR_AUTHORISATION :
+                       return -EPERM_INITIATOR_AUTHORISATION;
+               case ISCSI_STATUS_INITIATOR_ERROR_NOT_FOUND :
+               case ISCSI_STATUS_INITIATOR_ERROR_REMOVED :
+                       return -ENODEV;
+               default :
+                       return -ENOTSUP_INITIATOR_STATUS;
+               }
+       case ISCSI_STATUS_TARGET_ERROR :
+               switch ( status_detail ) {
+               case ISCSI_STATUS_TARGET_ERROR_UNAVAILABLE:
+                       return -EIO_TARGET_UNAVAILABLE;
+               case ISCSI_STATUS_TARGET_ERROR_NO_RESOURCES:
+                       return -EIO_TARGET_NO_RESOURCES;
+               default:
+                       return -ENOTSUP_TARGET_STATUS;
+               }
+       default :
+               return -EINVAL;
+       }
+}
+
+/**
+ * Receive data segment of an iSCSI login response PDU
+ *
+ * @v iscsi            iSCSI session
+ * @v data             Received data
+ * @v len              Length of received data
+ * @v remaining                Data remaining after this data
+ * @ret rc             Return status code
+ */
+static int iscsi_rx_login_response ( struct iscsi_session *iscsi,
+                                    const void *data, size_t len,
+                                    size_t remaining ) {
+       struct iscsi_bhs_login_response *response
+               = &iscsi->rx_bhs.login_response;
+       int rc;
+
+       /* Buffer up the PDU data */
+       if ( ( rc = iscsi_rx_buffered_data ( iscsi, data, len ) ) != 0 ) {
+               DBGC ( iscsi, "iSCSI %p could not buffer login response: %s\n",
+                      iscsi, strerror ( rc ) );
+               return rc;
+       }
+       if ( remaining )
+               return 0;
+
+       /* Process string data and discard string buffer */
+       if ( ( rc = iscsi_handle_strings ( iscsi, iscsi->rx_buffer,
+                                          iscsi->rx_len ) ) != 0 )
+               return rc;
+       iscsi_rx_buffered_data_done ( iscsi );
+
+       /* Check for login redirection */
+       if ( response->status_class == ISCSI_STATUS_REDIRECT ) {
+               DBGC ( iscsi, "iSCSI %p redirecting to new server\n", iscsi );
+               iscsi_close_connection ( iscsi, 0 );
+               if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 ) {
+                       DBGC ( iscsi, "iSCSI %p could not redirect: %s\n ",
+                              iscsi, strerror ( rc ) );
+                       return rc;
+               }
+               return 0;
+       }
+
+       /* Check for fatal errors */
+       if ( response->status_class != 0 ) {
+               DBGC ( iscsi, "iSCSI login failure: class %02x detail %02x\n",
+                      response->status_class, response->status_detail );
+               rc = iscsi_status_to_rc ( response->status_class,
+                                         response->status_detail );
+               return rc;
+       }
+
+       /* Handle login transitions */
+       if ( response->flags & ISCSI_LOGIN_FLAG_TRANSITION ) {
+               iscsi->status &= ~( ISCSI_STATUS_PHASE_MASK |
+                                   ISCSI_STATUS_STRINGS_MASK );
+               switch ( response->flags & ISCSI_LOGIN_NSG_MASK ) {
+               case ISCSI_LOGIN_NSG_OPERATIONAL_NEGOTIATION:
+                       iscsi->status |=
+                               ( ISCSI_STATUS_OPERATIONAL_NEGOTIATION_PHASE |
+                                 ISCSI_STATUS_STRINGS_OPERATIONAL );
+                       break;
+               case ISCSI_LOGIN_NSG_FULL_FEATURE_PHASE:
+                       iscsi->status |= ISCSI_STATUS_FULL_FEATURE_PHASE;
+                       break;
+               default:
+                       DBGC ( iscsi, "iSCSI %p got invalid response flags "
+                              "%02x\n", iscsi, response->flags );
+                       return -EIO;
+               }
+       }
+
+       /* Send next login request PDU if we haven't reached the full
+        * feature phase yet.
+        */
+       if ( ( iscsi->status & ISCSI_STATUS_PHASE_MASK ) !=
+            ISCSI_STATUS_FULL_FEATURE_PHASE ) {
+               iscsi_start_login ( iscsi );
+               return 0;
+       }
+
+       /* Check that target authentication was successful (if required) */
+       if ( ( iscsi->status & ISCSI_STATUS_AUTH_REVERSE_REQUIRED ) &&
+            ! ( iscsi->status & ISCSI_STATUS_AUTH_REVERSE_OK ) ) {
+               DBGC ( iscsi, "iSCSI %p nefarious target tried to bypass "
+                      "authentication\n", iscsi );
+               return -EPROTO;
+       }
+
+       /* Notify SCSI layer of window change */
+       DBGC ( iscsi, "iSCSI %p entering full feature phase\n", iscsi );
+       xfer_window_changed ( &iscsi->control );
+
+       return 0;
+}
+
+/****************************************************************************
+ *
+ * iSCSI to socket interface
+ *
+ */
+
+/**
+ * Pause TX engine
+ *
+ * @v iscsi            iSCSI session
+ */
+static void iscsi_tx_pause ( struct iscsi_session *iscsi ) {
+       process_del ( &iscsi->process );
+}
+
+/**
+ * Resume TX engine
+ *
+ * @v iscsi            iSCSI session
+ */
+static void iscsi_tx_resume ( struct iscsi_session *iscsi ) {
+       process_add ( &iscsi->process );
+}
+
+/**
+ * Start up a new TX PDU
+ *
+ * @v iscsi            iSCSI session
+ *
+ * This initiates the process of sending a new PDU.  Only one PDU may
+ * be in transit at any one time.
+ */
+static void iscsi_start_tx ( struct iscsi_session *iscsi ) {
+
+       assert ( iscsi->tx_state == ISCSI_TX_IDLE );
+
+       /* Initialise TX BHS */
+       memset ( &iscsi->tx_bhs, 0, sizeof ( iscsi->tx_bhs ) );
+
+       /* Flag TX engine to start transmitting */
+       iscsi->tx_state = ISCSI_TX_BHS;
+
+       /* Start transmission process */
+       iscsi_tx_resume ( iscsi );
+}
+
+/**
+ * Transmit nothing
+ *
+ * @v iscsi            iSCSI session
+ * @ret rc             Return status code
+ */
+static int iscsi_tx_nothing ( struct iscsi_session *iscsi __unused ) {
+       return 0;
+}
+
+/**
+ * Transmit basic header segment of an iSCSI PDU
+ *
+ * @v iscsi            iSCSI session
+ * @ret rc             Return status code
+ */
+static int iscsi_tx_bhs ( struct iscsi_session *iscsi ) {
+       return xfer_deliver_raw ( &iscsi->socket,  &iscsi->tx_bhs,
+                                 sizeof ( iscsi->tx_bhs ) );
+}
+
+/**
+ * Transmit data segment of an iSCSI PDU
+ *
+ * @v iscsi            iSCSI session
+ * @ret rc             Return status code
+ * 
+ * Handle transmission of part of a PDU data segment.  iscsi::tx_bhs
+ * will be valid when this is called.
+ */
+static int iscsi_tx_data ( struct iscsi_session *iscsi ) {
+       struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
+
+       switch ( common->opcode & ISCSI_OPCODE_MASK ) {
+       case ISCSI_OPCODE_DATA_OUT:
+               return iscsi_tx_data_out ( iscsi );
+       case ISCSI_OPCODE_LOGIN_REQUEST:
+               return iscsi_tx_login_request ( iscsi );
+       default:
+               /* Nothing to send in other states */
+               return 0;
+       }
+}
+
+/**
+ * Complete iSCSI PDU transmission
+ *
+ * @v iscsi            iSCSI session
+ *
+ * Called when a PDU has been completely transmitted and the TX state
+ * machine is about to enter the idle state.  iscsi::tx_bhs will be
+ * valid for the just-completed PDU when this is called.
+ */
+static void iscsi_tx_done ( struct iscsi_session *iscsi ) {
+       struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
+
+       /* Stop transmission process */
+       iscsi_tx_pause ( iscsi );
+
+       switch ( common->opcode & ISCSI_OPCODE_MASK ) {
+       case ISCSI_OPCODE_DATA_OUT:
+               iscsi_data_out_done ( iscsi );
+               break;
+       case ISCSI_OPCODE_LOGIN_REQUEST:
+               iscsi_login_request_done ( iscsi );
+               break;
+       default:
+               /* No action */
+               break;
+       }
+}
+
+/**
+ * Transmit iSCSI PDU
+ *
+ * @v iscsi            iSCSI session
+ * @v buf              Temporary data buffer
+ * @v len              Length of temporary data buffer
+ * 
+ * Constructs data to be sent for the current TX state
+ */
+static void iscsi_tx_step ( struct iscsi_session *iscsi ) {
+       struct iscsi_bhs_common *common = &iscsi->tx_bhs.common;
+       int ( * tx ) ( struct iscsi_session *iscsi );
+       enum iscsi_tx_state next_state;
+       size_t tx_len;
+       int rc;
+
+       /* Select fragment to transmit */
+       while ( 1 ) {
+               switch ( iscsi->tx_state ) {
+               case ISCSI_TX_BHS:
+                       tx = iscsi_tx_bhs;
+                       tx_len = sizeof ( iscsi->tx_bhs );
+                       next_state = ISCSI_TX_AHS;
+                       break;
+               case ISCSI_TX_AHS:
+                       tx = iscsi_tx_nothing;
+                       tx_len = 0;
+                       next_state = ISCSI_TX_DATA;
+                       break;
+               case ISCSI_TX_DATA:
+                       tx = iscsi_tx_data;
+                       tx_len = ISCSI_DATA_LEN ( common->lengths );
+                       next_state = ISCSI_TX_IDLE;
+                       break;
+               case ISCSI_TX_IDLE:
+                       /* Nothing to do; pause processing */
+                       iscsi_tx_pause ( iscsi );
+                       return;
+               default:
+                       assert ( 0 );
+                       return;
+               }
+
+               /* Check for window availability, if needed */
+               if ( tx_len && ( xfer_window ( &iscsi->socket ) == 0 ) ) {
+                       /* Cannot transmit at this point; pause
+                        * processing and wait for window to reopen
+                        */
+                       iscsi_tx_pause ( iscsi );
+                       return;
+               }
+
+               /* Transmit data */
+               if ( ( rc = tx ( iscsi ) ) != 0 ) {
+                       DBGC ( iscsi, "iSCSI %p could not transmit: %s\n",
+                              iscsi, strerror ( rc ) );
+                       /* Transmission errors are fatal */
+                       iscsi_close ( iscsi, rc );
+                       return;
+               }
+
+               /* Move to next state */
+               iscsi->tx_state = next_state;
+
+               /* If we have moved to the idle state, mark
+                * transmission as complete
+                */
+               if ( iscsi->tx_state == ISCSI_TX_IDLE )
+                       iscsi_tx_done ( iscsi );
+       }
+}
+
+/** iSCSI TX process descriptor */
+static struct process_descriptor iscsi_process_desc =
+       PROC_DESC ( struct iscsi_session, process, iscsi_tx_step );
+
+/**
+ * Receive basic header segment of an iSCSI PDU
+ *
+ * @v iscsi            iSCSI session
+ * @v data             Received data
+ * @v len              Length of received data
+ * @v remaining                Data remaining after this data
+ * @ret rc             Return status code
+ *
+ * This fills in iscsi::rx_bhs with the data from the BHS portion of
+ * the received PDU.
+ */
+static int iscsi_rx_bhs ( struct iscsi_session *iscsi, const void *data,
+                         size_t len, size_t remaining __unused ) {
+       memcpy ( &iscsi->rx_bhs.bytes[iscsi->rx_offset], data, len );
+       if ( ( iscsi->rx_offset + len ) >= sizeof ( iscsi->rx_bhs ) ) {
+               DBGC2 ( iscsi, "iSCSI %p received PDU opcode %#x len %#x\n",
+                       iscsi, iscsi->rx_bhs.common.opcode,
+                       ISCSI_DATA_LEN ( iscsi->rx_bhs.common.lengths ) );
+       }
+       return 0;
+}
+
+/**
+ * Discard portion of an iSCSI PDU.
+ *
+ * @v iscsi            iSCSI session
+ * @v data             Received data
+ * @v len              Length of received data
+ * @v remaining                Data remaining after this data
+ * @ret rc             Return status code
+ *
+ * This discards data from a portion of a received PDU.
+ */
+static int iscsi_rx_discard ( struct iscsi_session *iscsi __unused,
+                             const void *data __unused, size_t len __unused,
+                             size_t remaining __unused ) {
+       /* Do nothing */
+       return 0;
+}
+
+/**
+ * Receive data segment of an iSCSI PDU
+ *
+ * @v iscsi            iSCSI session
+ * @v data             Received data
+ * @v len              Length of received data
+ * @v remaining                Data remaining after this data
+ * @ret rc             Return status code
+ *
+ * Handle processing of part of a PDU data segment.  iscsi::rx_bhs
+ * will be valid when this is called.
+ */
+static int iscsi_rx_data ( struct iscsi_session *iscsi, const void *data,
+                          size_t len, size_t remaining ) {
+       struct iscsi_bhs_common_response *response
+               = &iscsi->rx_bhs.common_response;
+
+       /* Update cmdsn and statsn */
+       iscsi->cmdsn = ntohl ( response->expcmdsn );
+       iscsi->statsn = ntohl ( response->statsn );
+
+       switch ( response->opcode & ISCSI_OPCODE_MASK ) {
+       case ISCSI_OPCODE_LOGIN_RESPONSE:
+               return iscsi_rx_login_response ( iscsi, data, len, remaining );
+       case ISCSI_OPCODE_SCSI_RESPONSE:
+               return iscsi_rx_scsi_response ( iscsi, data, len, remaining );
+       case ISCSI_OPCODE_DATA_IN:
+               return iscsi_rx_data_in ( iscsi, data, len, remaining );
+       case ISCSI_OPCODE_R2T:
+               return iscsi_rx_r2t ( iscsi, data, len, remaining );
+       case ISCSI_OPCODE_NOP_IN:
+               return iscsi_rx_nop_in ( iscsi, data, len, remaining );
+       default:
+               if ( remaining )
+                       return 0;
+               DBGC ( iscsi, "iSCSI %p unknown opcode %02x\n", iscsi,
+                      response->opcode );
+               return -ENOTSUP_OPCODE;
+       }
+}
+
+/**
+ * Receive new data
+ *
+ * @v iscsi            iSCSI session
+ * @v iobuf            I/O buffer
+ * @v meta             Data transfer metadata
+ * @ret rc             Return status code
+ *
+ * This handles received PDUs.  The receive strategy is to fill in
+ * iscsi::rx_bhs with the contents of the BHS portion of the PDU,
+ * throw away any AHS portion, and then process each part of the data
+ * portion as it arrives.  The data processing routine therefore
+ * always has a full copy of the BHS available, even for portions of
+ * the data in different packets to the BHS.
+ */
+static int iscsi_socket_deliver ( struct iscsi_session *iscsi,
+                                 struct io_buffer *iobuf,
+                                 struct xfer_metadata *meta __unused ) {
+       struct iscsi_bhs_common *common = &iscsi->rx_bhs.common;
+       int ( * rx ) ( struct iscsi_session *iscsi, const void *data,
+                      size_t len, size_t remaining );
+       enum iscsi_rx_state next_state;
+       size_t frag_len;
+       size_t remaining;
+       int rc;
+
+       while ( 1 ) {
+               switch ( iscsi->rx_state ) {
+               case ISCSI_RX_BHS:
+                       rx = iscsi_rx_bhs;
+                       iscsi->rx_len = sizeof ( iscsi->rx_bhs );
+                       next_state = ISCSI_RX_AHS;                      
+                       break;
+               case ISCSI_RX_AHS:
+                       rx = iscsi_rx_discard;
+                       iscsi->rx_len = 4 * ISCSI_AHS_LEN ( common->lengths );
+                       next_state = ISCSI_RX_DATA;
+                       break;
+               case ISCSI_RX_DATA:
+                       rx = iscsi_rx_data;
+                       iscsi->rx_len = ISCSI_DATA_LEN ( common->lengths );
+                       next_state = ISCSI_RX_DATA_PADDING;
+                       break;
+               case ISCSI_RX_DATA_PADDING:
+                       rx = iscsi_rx_discard;
+                       iscsi->rx_len = ISCSI_DATA_PAD_LEN ( common->lengths );
+                       next_state = ISCSI_RX_BHS;
+                       break;
+               default:
+                       assert ( 0 );
+                       rc = -EINVAL;
+                       goto done;
+               }
+
+               frag_len = iscsi->rx_len - iscsi->rx_offset;
+               if ( frag_len > iob_len ( iobuf ) )
+                       frag_len = iob_len ( iobuf );
+               remaining = iscsi->rx_len - iscsi->rx_offset - frag_len;
+               if ( ( rc = rx ( iscsi, iobuf->data, frag_len,
+                                remaining ) ) != 0 ) {
+                       DBGC ( iscsi, "iSCSI %p could not process received "
+                              "data: %s\n", iscsi, strerror ( rc ) );
+                       goto done;
+               }
+
+               iscsi->rx_offset += frag_len;
+               iob_pull ( iobuf, frag_len );
+
+               /* If all the data for this state has not yet been
+                * received, stay in this state for now.
+                */
+               if ( iscsi->rx_offset != iscsi->rx_len ) {
+                       rc = 0;
+                       goto done;
+               }
+
+               iscsi->rx_state = next_state;
+               iscsi->rx_offset = 0;
+       }
+
+ done:
+       /* Free I/O buffer */
+       free_iob ( iobuf );
+
+       /* Destroy session on error */
+       if ( rc != 0 )
+               iscsi_close ( iscsi, rc );
+
+       return rc;
+}
+
+/**
+ * Handle redirection event
+ *
+ * @v iscsi            iSCSI session
+ * @v type             Location type
+ * @v args             Remaining arguments depend upon location type
+ * @ret rc             Return status code
+ */
+static int iscsi_vredirect ( struct iscsi_session *iscsi, int type,
+                            va_list args ) {
+       va_list tmp;
+       struct sockaddr *peer;
+       int rc;
+
+       /* Intercept redirects to a LOCATION_SOCKET and record the IP
+        * address for the iBFT.  This is a bit of a hack, but avoids
+        * inventing an ioctl()-style call to retrieve the socket
+        * address from a data-xfer interface.
+        */
+       if ( type == LOCATION_SOCKET ) {
+               va_copy ( tmp, args );
+               ( void ) va_arg ( tmp, int ); /* Discard "semantics" */
+               peer = va_arg ( tmp, struct sockaddr * );
+               memcpy ( &iscsi->target_sockaddr, peer,
+                        sizeof ( iscsi->target_sockaddr ) );
+               va_end ( tmp );
+       }
+
+       /* Redirect to new location */
+       if ( ( rc = xfer_vreopen ( &iscsi->socket, type, args ) ) != 0 )
+               goto err;
+
+       return 0;
+
+ err:
+       iscsi_close ( iscsi, rc );
+       return rc;
+}
+
+/** iSCSI socket interface operations */
+static struct interface_operation iscsi_socket_operations[] = {
+       INTF_OP ( xfer_deliver, struct iscsi_session *, iscsi_socket_deliver ),
+       INTF_OP ( xfer_window_changed, struct iscsi_session *,
+                 iscsi_tx_resume ),
+       INTF_OP ( xfer_vredirect, struct iscsi_session *, iscsi_vredirect ),
+       INTF_OP ( intf_close, struct iscsi_session *, iscsi_close ),
+};
+
+/** iSCSI socket interface descriptor */
+static struct interface_descriptor iscsi_socket_desc =
+       INTF_DESC ( struct iscsi_session, socket, iscsi_socket_operations );
+
+/****************************************************************************
+ *
+ * iSCSI command issuing
+ *
+ */
+
+/**
+ * Check iSCSI flow-control window
+ *
+ * @v iscsi            iSCSI session
+ * @ret len            Length of window
+ */
+static size_t iscsi_scsi_window ( struct iscsi_session *iscsi ) {
+
+       if ( ( ( iscsi->status & ISCSI_STATUS_PHASE_MASK ) ==
+              ISCSI_STATUS_FULL_FEATURE_PHASE ) &&
+            ( iscsi->command == NULL ) ) {
+               /* We cannot handle concurrent commands */
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
+/**
+ * Issue iSCSI SCSI command
+ *
+ * @v iscsi            iSCSI session
+ * @v parent           Parent interface
+ * @v command          SCSI command
+ * @ret tag            Command tag, or negative error
+ */
+static int iscsi_scsi_command ( struct iscsi_session *iscsi,
+                               struct interface *parent,
+                               struct scsi_cmd *command ) {
+
+       /* This iSCSI implementation cannot handle multiple concurrent
+        * commands or commands arriving before login is complete.
+        */
+       if ( iscsi_scsi_window ( iscsi ) == 0 ) {
+               DBGC ( iscsi, "iSCSI %p cannot handle concurrent commands\n",
+                      iscsi );
+               return -EOPNOTSUPP;
+       }
+
+       /* Store command */
+       iscsi->command = malloc ( sizeof ( *command ) );
+       if ( ! iscsi->command )
+               return -ENOMEM;
+       memcpy ( iscsi->command, command, sizeof ( *command ) );
+
+       /* Assign new ITT */
+       iscsi_new_itt ( iscsi );
+
+       /* Start sending command */
+       iscsi_start_command ( iscsi );
+
+       /* Attach to parent interface and return */
+       intf_plug_plug ( &iscsi->data, parent );
+       return iscsi->itt;
+}
+
+/**
+ * Get iSCSI ACPI descriptor
+ *
+ * @v iscsi            iSCSI session
+ * @ret desc           ACPI descriptor
+ */
+static struct acpi_descriptor * iscsi_describe ( struct iscsi_session *iscsi ) {
+
+       return &iscsi->desc;
+}
+
+/** iSCSI SCSI command-issuing interface operations */
+static struct interface_operation iscsi_control_op[] = {
+       INTF_OP ( scsi_command, struct iscsi_session *, iscsi_scsi_command ),
+       INTF_OP ( xfer_window, struct iscsi_session *, iscsi_scsi_window ),
+       INTF_OP ( intf_close, struct iscsi_session *, iscsi_close ),
+       INTF_OP ( acpi_describe, struct iscsi_session *, iscsi_describe ),
+};
+
+/** iSCSI SCSI command-issuing interface descriptor */
+static struct interface_descriptor iscsi_control_desc =
+       INTF_DESC ( struct iscsi_session, control, iscsi_control_op );
+
+/**
+ * Close iSCSI command
+ *
+ * @v iscsi            iSCSI session
+ * @v rc               Reason for close
+ */
+static void iscsi_command_close ( struct iscsi_session *iscsi, int rc ) {
+
+       /* Restart interface */
+       intf_restart ( &iscsi->data, rc );
+
+       /* Treat unsolicited command closures mid-command as fatal,
+        * because we have no code to handle partially-completed PDUs.
+        */
+       if ( iscsi->command != NULL )
+               iscsi_close ( iscsi, ( ( rc == 0 ) ? -ECANCELED : rc ) );
+}
+
+/** iSCSI SCSI command interface operations */
+static struct interface_operation iscsi_data_op[] = {
+       INTF_OP ( intf_close, struct iscsi_session *, iscsi_command_close ),
+};
+
+/** iSCSI SCSI command interface descriptor */
+static struct interface_descriptor iscsi_data_desc =
+       INTF_DESC ( struct iscsi_session, data, iscsi_data_op );
+
+/****************************************************************************
+ *
+ * Instantiator
+ *
+ */
+
+/** iSCSI root path components (as per RFC4173) */
+enum iscsi_root_path_component {
+       RP_SERVERNAME = 0,
+       RP_PROTOCOL,
+       RP_PORT,
+       RP_LUN,
+       RP_TARGETNAME,
+       NUM_RP_COMPONENTS
+};
+
+/** iSCSI initiator IQN setting */
+const struct setting initiator_iqn_setting __setting ( SETTING_SANBOOT_EXTRA,
+                                                      initiator-iqn ) = {
+       .name = "initiator-iqn",
+       .description = "iSCSI initiator name",
+       .tag = DHCP_ISCSI_INITIATOR_IQN,
+       .type = &setting_type_string,
+};
+
+/** iSCSI reverse username setting */
+const struct setting reverse_username_setting __setting ( SETTING_AUTH_EXTRA,
+                                                         reverse-username ) = {
+       .name = "reverse-username",
+       .description = "Reverse user name",
+       .tag = DHCP_EB_REVERSE_USERNAME,
+       .type = &setting_type_string,
+};
+
+/** iSCSI reverse password setting */
+const struct setting reverse_password_setting __setting ( SETTING_AUTH_EXTRA,
+                                                         reverse-password ) = {
+       .name = "reverse-password",
+       .description = "Reverse password",
+       .tag = DHCP_EB_REVERSE_PASSWORD,
+       .type = &setting_type_string,
+};
+
+/**
+ * Parse iSCSI root path
+ *
+ * @v iscsi            iSCSI session
+ * @v root_path                iSCSI root path (as per RFC4173)
+ * @ret rc             Return status code
+ */
+static int iscsi_parse_root_path ( struct iscsi_session *iscsi,
+                                  const char *root_path ) {
+       char rp_copy[ strlen ( root_path ) + 1 ];
+       char *rp_comp[NUM_RP_COMPONENTS];
+       char *rp = rp_copy;
+       int skip = 0;
+       int i = 0;
+       int rc;
+
+       /* Split root path into component parts */
+       strcpy ( rp_copy, root_path );
+       while ( 1 ) {
+               rp_comp[i++] = rp;
+               if ( i == NUM_RP_COMPONENTS )
+                       break;
+               for ( ; ( ( *rp != ':' ) || skip ) ; rp++ ) {
+                       if ( ! *rp ) {
+                               DBGC ( iscsi, "iSCSI %p root path \"%s\" "
+                                      "too short\n", iscsi, root_path );
+                               return -EINVAL_ROOT_PATH_TOO_SHORT;
+                       } else if ( *rp == '[' ) {
+                               skip = 1;
+                       } else if ( *rp == ']' ) {
+                               skip = 0;
+                       }
+               }
+               *(rp++) = '\0';
+       }
+
+       /* Use root path components to configure iSCSI session */
+       iscsi->target_address = strdup ( rp_comp[RP_SERVERNAME] );
+       if ( ! iscsi->target_address )
+               return -ENOMEM;
+       iscsi->target_port = strtoul ( rp_comp[RP_PORT], NULL, 10 );
+       if ( ! iscsi->target_port )
+               iscsi->target_port = ISCSI_PORT;
+       if ( ( rc = scsi_parse_lun ( rp_comp[RP_LUN], &iscsi->lun ) ) != 0 ) {
+               DBGC ( iscsi, "iSCSI %p invalid LUN \"%s\"\n",
+                      iscsi, rp_comp[RP_LUN] );
+               return rc;
+       }
+       iscsi->target_iqn = strdup ( rp_comp[RP_TARGETNAME] );
+       if ( ! iscsi->target_iqn )
+               return -ENOMEM;
+
+       return 0;
+}
+
+/**
+ * Fetch iSCSI settings
+ *
+ * @v iscsi            iSCSI session
+ * @ret rc             Return status code
+ */
+static int iscsi_fetch_settings ( struct iscsi_session *iscsi ) {
+       char *hostname;
+       union uuid uuid;
+       int len;
+
+       /* Fetch relevant settings.  Don't worry about freeing on
+        * error, since iscsi_free() will take care of that anyway.
+        */
+       fetch_string_setting_copy ( NULL, &username_setting,
+                                   &iscsi->initiator_username );
+       fetch_string_setting_copy ( NULL, &password_setting,
+                                   &iscsi->initiator_password );
+       fetch_string_setting_copy ( NULL, &reverse_username_setting,
+                                   &iscsi->target_username );
+       fetch_string_setting_copy ( NULL, &reverse_password_setting,
+                                   &iscsi->target_password );
+
+       /* Use explicit initiator IQN if provided */
+       fetch_string_setting_copy ( NULL, &initiator_iqn_setting,
+                                   &iscsi->initiator_iqn );
+       if ( iscsi->initiator_iqn )
+               return 0;
+
+       /* Otherwise, try to construct an initiator IQN from the hostname */
+       fetch_string_setting_copy ( NULL, &hostname_setting, &hostname );
+       if ( hostname ) {
+               len = asprintf ( &iscsi->initiator_iqn,
+                                ISCSI_DEFAULT_IQN_PREFIX ":%s", hostname );
+               free ( hostname );
+               if ( len < 0 ) {
+                       DBGC ( iscsi, "iSCSI %p could not allocate initiator "
+                              "IQN\n", iscsi );
+                       return -ENOMEM;
+               }
+               assert ( iscsi->initiator_iqn );
+               return 0;
+       }
+
+       /* Otherwise, try to construct an initiator IQN from the UUID */
+       if ( ( len = fetch_uuid_setting ( NULL, &uuid_setting, &uuid ) ) < 0 ) {
+               DBGC ( iscsi, "iSCSI %p has no suitable initiator IQN\n",
+                      iscsi );
+               return -EINVAL_NO_INITIATOR_IQN;
+       }
+       if ( ( len = asprintf ( &iscsi->initiator_iqn,
+                               ISCSI_DEFAULT_IQN_PREFIX ":%s",
+                               uuid_ntoa ( &uuid ) ) ) < 0 ) {
+               DBGC ( iscsi, "iSCSI %p could not allocate initiator IQN\n",
+                      iscsi );
+               return -ENOMEM;
+       }
+       assert ( iscsi->initiator_iqn );
+
+       return 0;
+}
+
+
+/**
+ * Check iSCSI authentication details
+ *
+ * @v iscsi            iSCSI session
+ * @ret rc             Return status code
+ */
+static int iscsi_check_auth ( struct iscsi_session *iscsi ) {
+
+       /* Check for invalid authentication combinations */
+       if ( ( /* Initiator username without password (or vice-versa) */
+               ( !! iscsi->initiator_username ) ^
+               ( !! iscsi->initiator_password ) ) ||
+            ( /* Target username without password (or vice-versa) */
+               ( !! iscsi->target_username ) ^
+               ( !! iscsi->target_password ) ) ||
+            ( /* Target (reverse) without initiator (forward) */
+               ( iscsi->target_username &&
+                 ( ! iscsi->initiator_username ) ) ) ) {
+               DBGC ( iscsi, "iSCSI %p invalid credentials: initiator "
+                      "%sname,%spw, target %sname,%spw\n", iscsi,
+                      ( iscsi->initiator_username ? "" : "no " ),
+                      ( iscsi->initiator_password ? "" : "no " ),
+                      ( iscsi->target_username ? "" : "no " ),
+                      ( iscsi->target_password ? "" : "no " ) );
+               return -EINVAL_BAD_CREDENTIAL_MIX;
+       }
+
+       return 0;
+}
+
+/**
+ * Open iSCSI URI
+ *
+ * @v parent           Parent interface
+ * @v uri              URI
+ * @ret rc             Return status code
+ */
+static int iscsi_open ( struct interface *parent, struct uri *uri ) {
+       struct iscsi_session *iscsi;
+       int rc;
+
+       /* Sanity check */
+       if ( ! uri->opaque ) {
+               rc = -EINVAL_NO_ROOT_PATH;
+               goto err_sanity_uri;
+       }
+
+       /* Allocate and initialise structure */
+       iscsi = zalloc ( sizeof ( *iscsi ) );
+       if ( ! iscsi ) {
+               rc = -ENOMEM;
+               goto err_zalloc;
+       }
+       ref_init ( &iscsi->refcnt, iscsi_free );
+       intf_init ( &iscsi->control, &iscsi_control_desc, &iscsi->refcnt );
+       intf_init ( &iscsi->data, &iscsi_data_desc, &iscsi->refcnt );
+       intf_init ( &iscsi->socket, &iscsi_socket_desc, &iscsi->refcnt );
+       process_init_stopped ( &iscsi->process, &iscsi_process_desc,
+                              &iscsi->refcnt );
+//     acpi_init ( &iscsi->desc, &ibft_model, &iscsi->refcnt );
+
+       /* Parse root path */
+       if ( ( rc = iscsi_parse_root_path ( iscsi, uri->opaque ) ) != 0 )
+               goto err_parse_root_path;
+       /* Set fields not specified by root path */
+       if ( ( rc = iscsi_fetch_settings ( iscsi ) ) != 0 )
+               goto err_fetch_settings;
+       /* Validate authentication */
+       if ( ( rc = iscsi_check_auth ( iscsi ) ) != 0 )
+               goto err_check_auth;
+
+       /* Sanity checks */
+       if ( ! iscsi->target_address ) {
+               DBGC ( iscsi, "iSCSI %p does not yet support discovery\n",
+                      iscsi );
+               rc = -ENOTSUP_DISCOVERY;
+               goto err_sanity_address;
+       }
+       if ( ! iscsi->target_iqn ) {
+               DBGC ( iscsi, "iSCSI %p no target address supplied in %s\n",
+                      iscsi, uri->opaque );
+               rc = -EINVAL_NO_TARGET_IQN;
+               goto err_sanity_iqn;
+       }
+       DBGC ( iscsi, "iSCSI %p initiator %s\n",iscsi, iscsi->initiator_iqn );
+       DBGC ( iscsi, "iSCSI %p target %s %s\n",
+              iscsi, iscsi->target_address, iscsi->target_iqn );
+
+       /* Open socket */
+       if ( ( rc = iscsi_open_connection ( iscsi ) ) != 0 )
+               goto err_open_connection;
+
+       /* Attach SCSI device to parent interface */
+       if ( ( rc = scsi_open ( parent, &iscsi->control,
+                               &iscsi->lun ) ) != 0 ) {
+               DBGC ( iscsi, "iSCSI %p could not create SCSI device: %s\n",
+                      iscsi, strerror ( rc ) );
+               goto err_scsi_open;
+       }
+
+       /* Mortalise self, and return */
+       ref_put ( &iscsi->refcnt );
+       return 0;
+       
+ err_scsi_open:
+ err_open_connection:
+ err_sanity_iqn:
+ err_sanity_address:
+ err_check_auth:
+ err_fetch_settings:
+ err_parse_root_path:
+       iscsi_close ( iscsi, rc );
+       ref_put ( &iscsi->refcnt );
+ err_zalloc:
+ err_sanity_uri:
+       return rc;
+}
+
+/** iSCSI URI opener */
+struct uri_opener iscsi_uri_opener __uri_opener = {
+       .scheme = "iscsi",
+       .open = iscsi_open,
+};
diff --git a/IPXE/ipxe_org_code/ipxe-3fe683e.tar.bz2 b/IPXE/ipxe_org_code/ipxe-3fe683e.tar.bz2
new file mode 100644 (file)
index 0000000..0f60c1e
Binary files /dev/null and b/IPXE/ipxe_org_code/ipxe-3fe683e.tar.bz2 differ
diff --git a/License/BSD-2-Clause-Patent.txt b/License/BSD-2-Clause-Patent.txt
new file mode 100644 (file)
index 0000000..3795caf
--- /dev/null
@@ -0,0 +1,51 @@
+Copyright (c) 2019, TianoCore and contributors.  All rights reserved.
+
+SPDX-License-Identifier: BSD-2-Clause-Patent
+
+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.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+Subject to the terms and conditions of this license, each copyright holder
+and contributor hereby grants to those receiving rights under this license
+a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+(except for failure to satisfy the conditions of this license) patent
+license to make, have made, use, offer to sell, sell, import, and otherwise
+transfer this software, where such license applies only to those patent
+claims, already acquired or hereafter acquired, licensable by such copyright
+holder or contributor that are necessarily infringed by:
+
+(a) their Contribution(s) (the licensed copyrights of copyright holders and
+    non-copyrightable additions of contributors, in source or binary form)
+    alone; or
+
+(b) combination of their Contribution(s) with the work of authorship to
+    which such Contribution(s) was added by such copyright holder or
+    contributor, if, at the time the Contribution is added, such addition
+    causes such combination to be necessarily infringed. The patent license
+    shall not apply to any other combinations which include the
+    Contribution.
+
+Except as expressly stated above, no rights or licenses from any copyright
+holder or contributor is granted under this license, whether expressly, by
+implication, estoppel or otherwise.
+
+DISCLAIMER
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDERS 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.
\ No newline at end of file
diff --git a/License/BSD.txt b/License/BSD.txt
new file mode 100644 (file)
index 0000000..dbdb05e
--- /dev/null
@@ -0,0 +1,30 @@
+BSD License
+
+For Zstandard software
+
+Copyright (c) 2016-present, Facebook, Inc. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification,
+are permitted provided that the following conditions are met:
+
+ * Redistributions of source code must retain the above copyright notice, this
+   list of conditions and the following disclaimer.
+
+ * Redistributions in binary form must reproduce the above copyright notice,
+   this list of conditions and the following disclaimer in the documentation
+   and/or other materials provided with the distribution.
+
+ * Neither the name Facebook nor the names of its contributors may be used to
+   endorse or promote products derived from this software without specific
+   prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS 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 COPYRIGHT HOLDER 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.
\ No newline at end of file
diff --git a/License/MIT.txt b/License/MIT.txt
new file mode 100644 (file)
index 0000000..f47dc84
--- /dev/null
@@ -0,0 +1,8 @@
+The MIT License (MIT)
+Copyright © 2020 <copyright holders>
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git a/License/gpl-2.0.txt b/License/gpl-2.0.txt
new file mode 100644 (file)
index 0000000..d159169
--- /dev/null
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, 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 Lesser 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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 Street, 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.
+
+  <signature of Ty Coon>, 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 Lesser General
+Public License instead of this License.
diff --git a/License/gpl-3.0.txt b/License/gpl-3.0.txt
new file mode 100644 (file)
index 0000000..f288702
--- /dev/null
@@ -0,0 +1,674 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+                            Preamble
+
+  The GNU General Public License is a free, copyleft license for
+software and other kinds of works.
+
+  The licenses for most software and other practical works are designed
+to take away your freedom to share and change the works.  By contrast,
+the GNU General Public License is intended to guarantee your freedom to
+share and change all versions of a program--to make sure it remains free
+software for all its users.  We, the Free Software Foundation, use the
+GNU General Public License for most of our software; it applies also to
+any other work released this way by its authors.  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
+them 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 prevent others from denying you
+these rights or asking you to surrender the rights.  Therefore, you have
+certain responsibilities if you distribute copies of the software, or if
+you modify it: responsibilities to respect the freedom of others.
+
+  For example, if you distribute copies of such a program, whether
+gratis or for a fee, you must pass on to the recipients the same
+freedoms that you received.  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.
+
+  Developers that use the GNU GPL protect your rights with two steps:
+(1) assert copyright on the software, and (2) offer you this License
+giving you legal permission to copy, distribute and/or modify it.
+
+  For the developers' and authors' protection, the GPL clearly explains
+that there is no warranty for this free software.  For both users' and
+authors' sake, the GPL requires that modified versions be marked as
+changed, so that their problems will not be attributed erroneously to
+authors of previous versions.
+
+  Some devices are designed to deny users access to install or run
+modified versions of the software inside them, although the manufacturer
+can do so.  This is fundamentally incompatible with the aim of
+protecting users' freedom to change the software.  The systematic
+pattern of such abuse occurs in the area of products for individuals to
+use, which is precisely where it is most unacceptable.  Therefore, we
+have designed this version of the GPL to prohibit the practice for those
+products.  If such problems arise substantially in other domains, we
+stand ready to extend this provision to those domains in future versions
+of the GPL, as needed to protect the freedom of users.
+
+  Finally, every program is threatened constantly by software patents.
+States should not allow patents to restrict development and use of
+software on general-purpose computers, but in those that do, we wish to
+avoid the special danger that patents applied to a free program could
+make it effectively proprietary.  To prevent this, the GPL assures that
+patents cannot be used to render the program non-free.
+
+  The precise terms and conditions for copying, distribution and
+modification follow.
+
+                       TERMS AND CONDITIONS
+
+  0. Definitions.
+
+  "This License" refers to version 3 of the GNU General Public License.
+
+  "Copyright" also means copyright-like laws that apply to other kinds of
+works, such as semiconductor masks.
+
+  "The Program" refers to any copyrightable work licensed under this
+License.  Each licensee is addressed as "you".  "Licensees" and
+"recipients" may be individuals or organizations.
+
+  To "modify" a work means to copy from or adapt all or part of the work
+in a fashion requiring copyright permission, other than the making of an
+exact copy.  The resulting work is called a "modified version" of the
+earlier work or a work "based on" the earlier work.
+
+  A "covered work" means either the unmodified Program or a work based
+on the Program.
+
+  To "propagate" a work means to do anything with it that, without
+permission, would make you directly or secondarily liable for
+infringement under applicable copyright law, except executing it on a
+computer or modifying a private copy.  Propagation includes copying,
+distribution (with or without modification), making available to the
+public, and in some countries other activities as well.
+
+  To "convey" a work means any kind of propagation that enables other
+parties to make or receive copies.  Mere interaction with a user through
+a computer network, with no transfer of a copy, is not conveying.
+
+  An interactive user interface displays "Appropriate Legal Notices"
+to the extent that it includes a convenient and prominently visible
+feature that (1) displays an appropriate copyright notice, and (2)
+tells the user that there is no warranty for the work (except to the
+extent that warranties are provided), that licensees may convey the
+work under this License, and how to view a copy of this License.  If
+the interface presents a list of user commands or options, such as a
+menu, a prominent item in the list meets this criterion.
+
+  1. Source Code.
+
+  The "source code" for a work means the preferred form of the work
+for making modifications to it.  "Object code" means any non-source
+form of a work.
+
+  A "Standard Interface" means an interface that either is an official
+standard defined by a recognized standards body, or, in the case of
+interfaces specified for a particular programming language, one that
+is widely used among developers working in that language.
+
+  The "System Libraries" of an executable work include anything, other
+than the work as a whole, that (a) is included in the normal form of
+packaging a Major Component, but which is not part of that Major
+Component, and (b) serves only to enable use of the work with that
+Major Component, or to implement a Standard Interface for which an
+implementation is available to the public in source code form.  A
+"Major Component", in this context, means a major essential component
+(kernel, window system, and so on) of the specific operating system
+(if any) on which the executable work runs, or a compiler used to
+produce the work, or an object code interpreter used to run it.
+
+  The "Corresponding Source" for a work in object code form means all
+the source code needed to generate, install, and (for an executable
+work) run the object code and to modify the work, including scripts to
+control those activities.  However, it does not include the work's
+System Libraries, or general-purpose tools or generally available free
+programs which are used unmodified in performing those activities but
+which are not part of the work.  For example, Corresponding Source
+includes interface definition files associated with source files for
+the work, and the source code for shared libraries and dynamically
+linked subprograms that the work is specifically designed to require,
+such as by intimate data communication or control flow between those
+subprograms and other parts of the work.
+
+  The Corresponding Source need not include anything that users
+can regenerate automatically from other parts of the Corresponding
+Source.
+
+  The Corresponding Source for a work in source code form is that
+same work.
+
+  2. Basic Permissions.
+
+  All rights granted under this License are granted for the term of
+copyright on the Program, and are irrevocable provided the stated
+conditions are met.  This License explicitly affirms your unlimited
+permission to run the unmodified Program.  The output from running a
+covered work is covered by this License only if the output, given its
+content, constitutes a covered work.  This License acknowledges your
+rights of fair use or other equivalent, as provided by copyright law.
+
+  You may make, run and propagate covered works that you do not
+convey, without conditions so long as your license otherwise remains
+in force.  You may convey covered works to others for the sole purpose
+of having them make modifications exclusively for you, or provide you
+with facilities for running those works, provided that you comply with
+the terms of this License in conveying all material for which you do
+not control copyright.  Those thus making or running the covered works
+for you must do so exclusively on your behalf, under your direction
+and control, on terms that prohibit them from making any copies of
+your copyrighted material outside their relationship with you.
+
+  Conveying under any other circumstances is permitted solely under
+the conditions stated below.  Sublicensing is not allowed; section 10
+makes it unnecessary.
+
+  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
+
+  No covered work shall be deemed part of an effective technological
+measure under any applicable law fulfilling obligations under article
+11 of the WIPO copyright treaty adopted on 20 December 1996, or
+similar laws prohibiting or restricting circumvention of such
+measures.
+
+  When you convey a covered work, you waive any legal power to forbid
+circumvention of technological measures to the extent such circumvention
+is effected by exercising rights under this License with respect to
+the covered work, and you disclaim any intention to limit operation or
+modification of the work as a means of enforcing, against the work's
+users, your or third parties' legal rights to forbid circumvention of
+technological measures.
+
+  4. Conveying Verbatim Copies.
+
+  You may convey 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;
+keep intact all notices stating that this License and any
+non-permissive terms added in accord with section 7 apply to the code;
+keep intact all notices of the absence of any warranty; and give all
+recipients a copy of this License along with the Program.
+
+  You may charge any price or no price for each copy that you convey,
+and you may offer support or warranty protection for a fee.
+
+  5. Conveying Modified Source Versions.
+
+  You may convey a work based on the Program, or the modifications to
+produce it from the Program, in the form of source code under the
+terms of section 4, provided that you also meet all of these conditions:
+
+    a) The work must carry prominent notices stating that you modified
+    it, and giving a relevant date.
+
+    b) The work must carry prominent notices stating that it is
+    released under this License and any conditions added under section
+    7.  This requirement modifies the requirement in section 4 to
+    "keep intact all notices".
+
+    c) You must license the entire work, as a whole, under this
+    License to anyone who comes into possession of a copy.  This
+    License will therefore apply, along with any applicable section 7
+    additional terms, to the whole of the work, and all its parts,
+    regardless of how they are packaged.  This License gives no
+    permission to license the work in any other way, but it does not
+    invalidate such permission if you have separately received it.
+
+    d) If the work has interactive user interfaces, each must display
+    Appropriate Legal Notices; however, if the Program has interactive
+    interfaces that do not display Appropriate Legal Notices, your
+    work need not make them do so.
+
+  A compilation of a covered work with other separate and independent
+works, which are not by their nature extensions of the covered work,
+and which are not combined with it such as to form a larger program,
+in or on a volume of a storage or distribution medium, is called an
+"aggregate" if the compilation and its resulting copyright are not
+used to limit the access or legal rights of the compilation's users
+beyond what the individual works permit.  Inclusion of a covered work
+in an aggregate does not cause this License to apply to the other
+parts of the aggregate.
+
+  6. Conveying Non-Source Forms.
+
+  You may convey a covered work in object code form under the terms
+of sections 4 and 5, provided that you also convey the
+machine-readable Corresponding Source under the terms of this License,
+in one of these ways:
+
+    a) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by the
+    Corresponding Source fixed on a durable physical medium
+    customarily used for software interchange.
+
+    b) Convey the object code in, or embodied in, a physical product
+    (including a physical distribution medium), accompanied by a
+    written offer, valid for at least three years and valid for as
+    long as you offer spare parts or customer support for that product
+    model, to give anyone who possesses the object code either (1) a
+    copy of the Corresponding Source for all the software in the
+    product that is covered by this License, on a durable physical
+    medium customarily used for software interchange, for a price no
+    more than your reasonable cost of physically performing this
+    conveying of source, or (2) access to copy the
+    Corresponding Source from a network server at no charge.
+
+    c) Convey individual copies of the object code with a copy of the
+    written offer to provide the Corresponding Source.  This
+    alternative is allowed only occasionally and noncommercially, and
+    only if you received the object code with such an offer, in accord
+    with subsection 6b.
+
+    d) Convey the object code by offering access from a designated
+    place (gratis or for a charge), and offer equivalent access to the
+    Corresponding Source in the same way through the same place at no
+    further charge.  You need not require recipients to copy the
+    Corresponding Source along with the object code.  If the place to
+    copy the object code is a network server, the Corresponding Source
+    may be on a different server (operated by you or a third party)
+    that supports equivalent copying facilities, provided you maintain
+    clear directions next to the object code saying where to find the
+    Corresponding Source.  Regardless of what server hosts the
+    Corresponding Source, you remain obligated to ensure that it is
+    available for as long as needed to satisfy these requirements.
+
+    e) Convey the object code using peer-to-peer transmission, provided
+    you inform other peers where the object code and Corresponding
+    Source of the work are being offered to the general public at no
+    charge under subsection 6d.
+
+  A separable portion of the object code, whose source code is excluded
+from the Corresponding Source as a System Library, need not be
+included in conveying the object code work.
+
+  A "User Product" is either (1) a "consumer product", which means any
+tangible personal property which is normally used for personal, family,
+or household purposes, or (2) anything designed or sold for incorporation
+into a dwelling.  In determining whether a product is a consumer product,
+doubtful cases shall be resolved in favor of coverage.  For a particular
+product received by a particular user, "normally used" refers to a
+typical or common use of that class of product, regardless of the status
+of the particular user or of the way in which the particular user
+actually uses, or expects or is expected to use, the product.  A product
+is a consumer product regardless of whether the product has substantial
+commercial, industrial or non-consumer uses, unless such uses represent
+the only significant mode of use of the product.
+
+  "Installation Information" for a User Product means any methods,
+procedures, authorization keys, or other information required to install
+and execute modified versions of a covered work in that User Product from
+a modified version of its Corresponding Source.  The information must
+suffice to ensure that the continued functioning of the modified object
+code is in no case prevented or interfered with solely because
+modification has been made.
+
+  If you convey an object code work under this section in, or with, or
+specifically for use in, a User Product, and the conveying occurs as
+part of a transaction in which the right of possession and use of the
+User Product is transferred to the recipient in perpetuity or for a
+fixed term (regardless of how the transaction is characterized), the
+Corresponding Source conveyed under this section must be accompanied
+by the Installation Information.  But this requirement does not apply
+if neither you nor any third party retains the ability to install
+modified object code on the User Product (for example, the work has
+been installed in ROM).
+
+  The requirement to provide Installation Information does not include a
+requirement to continue to provide support service, warranty, or updates
+for a work that has been modified or installed by the recipient, or for
+the User Product in which it has been modified or installed.  Access to a
+network may be denied when the modification itself materially and
+adversely affects the operation of the network or violates the rules and
+protocols for communication across the network.
+
+  Corresponding Source conveyed, and Installation Information provided,
+in accord with this section must be in a format that is publicly
+documented (and with an implementation available to the public in
+source code form), and must require no special password or key for
+unpacking, reading or copying.
+
+  7. Additional Terms.
+
+  "Additional permissions" are terms that supplement the terms of this
+License by making exceptions from one or more of its conditions.
+Additional permissions that are applicable to the entire Program shall
+be treated as though they were included in this License, to the extent
+that they are valid under applicable law.  If additional permissions
+apply only to part of the Program, that part may be used separately
+under those permissions, but the entire Program remains governed by
+this License without regard to the additional permissions.
+
+  When you convey a copy of a covered work, you may at your option
+remove any additional permissions from that copy, or from any part of
+it.  (Additional permissions may be written to require their own
+removal in certain cases when you modify the work.)  You may place
+additional permissions on material, added by you to a covered work,
+for which you have or can give appropriate copyright permission.
+
+  Notwithstanding any other provision of this License, for material you
+add to a covered work, you may (if authorized by the copyright holders of
+that material) supplement the terms of this License with terms:
+
+    a) Disclaiming warranty or limiting liability differently from the
+    terms of sections 15 and 16 of this License; or
+
+    b) Requiring preservation of specified reasonable legal notices or
+    author attributions in that material or in the Appropriate Legal
+    Notices displayed by works containing it; or
+
+    c) Prohibiting misrepresentation of the origin of that material, or
+    requiring that modified versions of such material be marked in
+    reasonable ways as different from the original version; or
+
+    d) Limiting the use for publicity purposes of names of licensors or
+    authors of the material; or
+
+    e) Declining to grant rights under trademark law for use of some
+    trade names, trademarks, or service marks; or
+
+    f) Requiring indemnification of licensors and authors of that
+    material by anyone who conveys the material (or modified versions of
+    it) with contractual assumptions of liability to the recipient, for
+    any liability that these contractual assumptions directly impose on
+    those licensors and authors.
+
+  All other non-permissive additional terms are considered "further
+restrictions" within the meaning of section 10.  If the Program as you
+received it, or any part of it, contains a notice stating that it is
+governed by this License along with a term that is a further
+restriction, you may remove that term.  If a license document contains
+a further restriction but permits relicensing or conveying under this
+License, you may add to a covered work material governed by the terms
+of that license document, provided that the further restriction does
+not survive such relicensing or conveying.
+
+  If you add terms to a covered work in accord with this section, you
+must place, in the relevant source files, a statement of the
+additional terms that apply to those files, or a notice indicating
+where to find the applicable terms.
+
+  Additional terms, permissive or non-permissive, may be stated in the
+form of a separately written license, or stated as exceptions;
+the above requirements apply either way.
+
+  8. Termination.
+
+  You may not propagate or modify a covered work except as expressly
+provided under this License.  Any attempt otherwise to propagate or
+modify it is void, and will automatically terminate your rights under
+this License (including any patent licenses granted under the third
+paragraph of section 11).
+
+  However, if you cease all violation of this License, then your
+license from a particular copyright holder is reinstated (a)
+provisionally, unless and until the copyright holder explicitly and
+finally terminates your license, and (b) permanently, if the copyright
+holder fails to notify you of the violation by some reasonable means
+prior to 60 days after the cessation.
+
+  Moreover, your license from a particular copyright holder is
+reinstated permanently if the copyright holder notifies you of the
+violation by some reasonable means, this is the first time you have
+received notice of violation of this License (for any work) from that
+copyright holder, and you cure the violation prior to 30 days after
+your receipt of the notice.
+
+  Termination of your rights under this section does not terminate the
+licenses of parties who have received copies or rights from you under
+this License.  If your rights have been terminated and not permanently
+reinstated, you do not qualify to receive new licenses for the same
+material under section 10.
+
+  9. Acceptance Not Required for Having Copies.
+
+  You are not required to accept this License in order to receive or
+run a copy of the Program.  Ancillary propagation of a covered work
+occurring solely as a consequence of using peer-to-peer transmission
+to receive a copy likewise does not require acceptance.  However,
+nothing other than this License grants you permission to propagate or
+modify any covered work.  These actions infringe copyright if you do
+not accept this License.  Therefore, by modifying or propagating a
+covered work, you indicate your acceptance of this License to do so.
+
+  10. Automatic Licensing of Downstream Recipients.
+
+  Each time you convey a covered work, the recipient automatically
+receives a license from the original licensors, to run, modify and
+propagate that work, subject to this License.  You are not responsible
+for enforcing compliance by third parties with this License.
+
+  An "entity transaction" is a transaction transferring control of an
+organization, or substantially all assets of one, or subdividing an
+organization, or merging organizations.  If propagation of a covered
+work results from an entity transaction, each party to that
+transaction who receives a copy of the work also receives whatever
+licenses to the work the party's predecessor in interest had or could
+give under the previous paragraph, plus a right to possession of the
+Corresponding Source of the work from the predecessor in interest, if
+the predecessor has it or can get it with reasonable efforts.
+
+  You may not impose any further restrictions on the exercise of the
+rights granted or affirmed under this License.  For example, you may
+not impose a license fee, royalty, or other charge for exercise of
+rights granted under this License, and you may not initiate litigation
+(including a cross-claim or counterclaim in a lawsuit) alleging that
+any patent claim is infringed by making, using, selling, offering for
+sale, or importing the Program or any portion of it.
+
+  11. Patents.
+
+  A "contributor" is a copyright holder who authorizes use under this
+License of the Program or a work on which the Program is based.  The
+work thus licensed is called the contributor's "contributor version".
+
+  A contributor's "essential patent claims" are all patent claims
+owned or controlled by the contributor, whether already acquired or
+hereafter acquired, that would be infringed by some manner, permitted
+by this License, of making, using, or selling its contributor version,
+but do not include claims that would be infringed only as a
+consequence of further modification of the contributor version.  For
+purposes of this definition, "control" includes the right to grant
+patent sublicenses in a manner consistent with the requirements of
+this License.
+
+  Each contributor grants you a non-exclusive, worldwide, royalty-free
+patent license under the contributor's essential patent claims, to
+make, use, sell, offer for sale, import and otherwise run, modify and
+propagate the contents of its contributor version.
+
+  In the following three paragraphs, a "patent license" is any express
+agreement or commitment, however denominated, not to enforce a patent
+(such as an express permission to practice a patent or covenant not to
+sue for patent infringement).  To "grant" such a patent license to a
+party means to make such an agreement or commitment not to enforce a
+patent against the party.
+
+  If you convey a covered work, knowingly relying on a patent license,
+and the Corresponding Source of the work is not available for anyone
+to copy, free of charge and under the terms of this License, through a
+publicly available network server or other readily accessible means,
+then you must either (1) cause the Corresponding Source to be so
+available, or (2) arrange to deprive yourself of the benefit of the
+patent license for this particular work, or (3) arrange, in a manner
+consistent with the requirements of this License, to extend the patent
+license to downstream recipients.  "Knowingly relying" means you have
+actual knowledge that, but for the patent license, your conveying the
+covered work in a country, or your recipient's use of the covered work
+in a country, would infringe one or more identifiable patents in that
+country that you have reason to believe are valid.
+
+  If, pursuant to or in connection with a single transaction or
+arrangement, you convey, or propagate by procuring conveyance of, a
+covered work, and grant a patent license to some of the parties
+receiving the covered work authorizing them to use, propagate, modify
+or convey a specific copy of the covered work, then the patent license
+you grant is automatically extended to all recipients of the covered
+work and works based on it.
+
+  A patent license is "discriminatory" if it does not include within
+the scope of its coverage, prohibits the exercise of, or is
+conditioned on the non-exercise of one or more of the rights that are
+specifically granted under this License.  You may not convey a covered
+work if you are a party to an arrangement with a third party that is
+in the business of distributing software, under which you make payment
+to the third party based on the extent of your activity of conveying
+the work, and under which the third party grants, to any of the
+parties who would receive the covered work from you, a discriminatory
+patent license (a) in connection with copies of the covered work
+conveyed by you (or copies made from those copies), or (b) primarily
+for and in connection with specific products or compilations that
+contain the covered work, unless you entered into that arrangement,
+or that patent license was granted, prior to 28 March 2007.
+
+  Nothing in this License shall be construed as excluding or limiting
+any implied license or other defenses to infringement that may
+otherwise be available to you under applicable patent law.
+
+  12. No Surrender of Others' Freedom.
+
+  If 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 convey a
+covered work so as to satisfy simultaneously your obligations under this
+License and any other pertinent obligations, then as a consequence you may
+not convey it at all.  For example, if you agree to terms that obligate you
+to collect a royalty for further conveying from those to whom you convey
+the Program, the only way you could satisfy both those terms and this
+License would be to refrain entirely from conveying the Program.
+
+  13. Use with the GNU Affero General Public License.
+
+  Notwithstanding any other provision of this License, you have
+permission to link or combine any covered work with a work licensed
+under version 3 of the GNU Affero General Public License into a single
+combined work, and to convey the resulting work.  The terms of this
+License will continue to apply to the part which is the covered work,
+but the special requirements of the GNU Affero General Public License,
+section 13, concerning interaction through a network will apply to the
+combination as such.
+
+  14. Revised Versions of this License.
+
+  The Free Software Foundation may publish revised and/or new versions of
+the GNU 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 that a certain numbered version of the GNU General
+Public License "or any later version" applies to it, you have the
+option of following the terms and conditions either of that numbered
+version or of any later version published by the Free Software
+Foundation.  If the Program does not specify a version number of the
+GNU General Public License, you may choose any version ever published
+by the Free Software Foundation.
+
+  If the Program specifies that a proxy can decide which future
+versions of the GNU General Public License can be used, that proxy's
+public statement of acceptance of a version permanently authorizes you
+to choose that version for the Program.
+
+  Later license versions may give you additional or different
+permissions.  However, no additional obligations are imposed on any
+author or copyright holder as a result of your choosing to follow a
+later version.
+
+  15. Disclaimer of Warranty.
+
+  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.
+
+  16. Limitation of Liability.
+
+  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
+WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
+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.
+
+  17. Interpretation of Sections 15 and 16.
+
+  If the disclaimer of warranty and limitation of liability provided
+above cannot be given local legal effect according to their terms,
+reviewing courts shall apply local law that most closely approximates
+an absolute waiver of all civil liability in connection with the
+Program, unless a warranty or assumption of liability accompanies a
+copy of the Program in return for a fee.
+
+                     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
+state the exclusion of warranty; and each file should have at least
+the "copyright" line and a pointer to where the full notice is found.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 3 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, see <https://www.gnu.org/licenses/>.
+
+Also add information on how to contact you by electronic and paper mail.
+
+  If the program does terminal interaction, make it output a short
+notice like this when it starts in an interactive mode:
+
+    <program>  Copyright (C) <year>  <name of author>
+    This program 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, your program's commands
+might be different; for a GUI interface, you would use an "about box".
+
+  You should also get your employer (if you work as a programmer) or school,
+if any, to sign a "copyright disclaimer" for the program, if necessary.
+For more information on this, and how to apply and follow the GNU GPL, see
+<https://www.gnu.org/licenses/>.
+
+  The GNU 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 Lesser General
+Public License instead of this License.  But first, please read
+<https://www.gnu.org/licenses/why-not-lgpl.html>.
diff --git a/License/license-busybox.txt b/License/license-busybox.txt
new file mode 100644 (file)
index 0000000..65dfcb4
--- /dev/null
@@ -0,0 +1,3 @@
+BusyBox follows GPLv2 license (see gpl-2.0.txt)
+
+Ventoy does not modify its source code, only its binary is used.
diff --git a/License/license-device-mapper.txt b/License/license-device-mapper.txt
new file mode 100644 (file)
index 0000000..e8ffffa
--- /dev/null
@@ -0,0 +1,4 @@
+
+Device Mapper follows GPLv2 License (see gpl-2.0.txt)
+
+Ventoy modify its source code, these code modified by Ventoy follows the same license as Device Mapper.
diff --git a/License/license-edk2.txt b/License/license-edk2.txt
new file mode 100644 (file)
index 0000000..849aa70
--- /dev/null
@@ -0,0 +1,2 @@
+EDK2 follows BSD-2-Clause Plus Patent License (see BSD-2-Clause-Patent.txt)
+Ventoy add an uefi application module in MdeModulePkg, the code is developed by ventoy and is under GPLv3+ License (see gpl-3.0.txt).
diff --git a/License/license-efifs.txt b/License/license-efifs.txt
new file mode 100644 (file)
index 0000000..3fd3627
--- /dev/null
@@ -0,0 +1,2 @@
+efifs follows GPLv3 (see gpl-3.0.txt) License.
+Ventoy modify its source code, these code modified by Ventoy follows the same license as efifs.
diff --git a/License/license-exfat.txt b/License/license-exfat.txt
new file mode 100644 (file)
index 0000000..a4c7a17
--- /dev/null
@@ -0,0 +1,2 @@
+exfat follows GPLv2 (see gpl-2.0.txt) License.
+Ventoy modify its source code, these code modified by Ventoy follows the same license as exfat.
diff --git a/License/license-fat-filelib.txt b/License/license-fat-filelib.txt
new file mode 100644 (file)
index 0000000..73b8c28
--- /dev/null
@@ -0,0 +1,3 @@
+fat-filelib follows GPL license (see gpl-3.0.txt)
+
+Ventoy does not modify its source code, only its library is used.
diff --git a/License/license-fatfs.txt b/License/license-fatfs.txt
new file mode 100644 (file)
index 0000000..83abcac
--- /dev/null
@@ -0,0 +1,4 @@
+Therefore FatFs license is one of the BSD-style licenses but there is a significant feature. FatFs is mainly intended for embedded systems. In order to extend the usability for commercial products, the redistributions of FatFs in binary form, such as embedded code, binary library and any forms without source code, does not need to include about FatFs in the documentations. This is equivalent to the 1-clause BSD license. Of course FatFs is compatible with the most of open source software licenses including GNU GPL. When you redistribute the FatFs source code with any changes or create a fork, the license can also be changed to GNU GPL, BSD-style license or any open source software license that not conflict with FatFs license.
+
+Ventoy modify its source code, these code modified by Ventoy follow the same license as FatFs.
+
diff --git a/License/license-grub2.txt b/License/license-grub2.txt
new file mode 100644 (file)
index 0000000..7d4dfd7
--- /dev/null
@@ -0,0 +1,3 @@
+GRUB2 follows GPLv3+ license (see gpl-3.0.txt)
+Ventoy modify its source code, these code modified by Ventoy follow the same license as grub2.
+
diff --git a/License/license-imdisk.txt b/License/license-imdisk.txt
new file mode 100644 (file)
index 0000000..72122f6
--- /dev/null
@@ -0,0 +1,4 @@
+imdisk follows GPL license (see gpl-3.0.txt)
+
+Ventoy does not modify its source code, only its binary is used.
+
diff --git a/License/license-ipxe.txt b/License/license-ipxe.txt
new file mode 100644 (file)
index 0000000..7045ba9
--- /dev/null
@@ -0,0 +1,2 @@
+iPXE follows GPLv2 (see gpl-2.0.txt) or Unmodified Binary Distribution Licence (see ubdl.txt)
+Ventoy modify its source code, these code modified by Ventoy follow the same license as iPXE.
diff --git a/License/license-libfuse.txt b/License/license-libfuse.txt
new file mode 100644 (file)
index 0000000..5b3759c
--- /dev/null
@@ -0,0 +1,3 @@
+libfuse follows LGPL license (see lgpl.txt)
+
+Ventoy does not modify its source code, only its library is used.
diff --git a/License/license-liblzma.txt b/License/license-liblzma.txt
new file mode 100644 (file)
index 0000000..7a7b7b2
--- /dev/null
@@ -0,0 +1,3 @@
+liblzma follows LGPL license (see lgpl.txt)
+
+Ventoy does not modify its source code, only its library is used.
diff --git a/License/license-lz4.txt b/License/license-lz4.txt
new file mode 100644 (file)
index 0000000..dc4fd58
--- /dev/null
@@ -0,0 +1,5 @@
+
+Ventoy does not modify its source code, only its library is used.
+
+The library source of lz4 follows the BSD 2-Clause License.
+
diff --git a/License/license-lzo.txt b/License/license-lzo.txt
new file mode 100644 (file)
index 0000000..28f3456
--- /dev/null
@@ -0,0 +1,3 @@
+lzo follows GPLv2+ license (see gpl-2.0.txt)
+
+Ventoy does not modify its source code, only its library is used.
diff --git a/License/license-rufus.txt b/License/license-rufus.txt
new file mode 100644 (file)
index 0000000..fb2244d
--- /dev/null
@@ -0,0 +1,3 @@
+Rufus follows GPLv3+ license (see gpl-3.0.txt)
+Ventoy uses some function from Rufus, and all the Ventoy's code also follow GPLv3+ license.
+
diff --git a/License/license-smallz4.txt b/License/license-smallz4.txt
new file mode 100644 (file)
index 0000000..956869f
--- /dev/null
@@ -0,0 +1,4 @@
+smalLz4 follows MIT license (see MIT.txt)
+
+Ventoy does not modify its source code, only its binary is used.
+
diff --git a/License/license-squashfs-tools.txt b/License/license-squashfs-tools.txt
new file mode 100644 (file)
index 0000000..bf9785e
--- /dev/null
@@ -0,0 +1,4 @@
+
+squashfs-tools follows the GPLv2 License (see gpl-2.0.txt).
+Ventoy modify its source code, these code modified by Ventoy follow the same license as squashfs-tools.
+
diff --git a/License/license-vblade.txt b/License/license-vblade.txt
new file mode 100644 (file)
index 0000000..a0b45cb
--- /dev/null
@@ -0,0 +1,3 @@
+vblade follows GPLv2 license (see gpl-2.0.txt)
+Ventoy modify its source code, these code modified by Ventoy follow the same license as vblade.
+
diff --git a/License/license-ventoy.txt b/License/license-ventoy.txt
new file mode 100644 (file)
index 0000000..f6c5012
--- /dev/null
@@ -0,0 +1 @@
+All the modules and tools developed by Ventoy are under GPLv3+ (see gpl-3.0.txt) License.
diff --git a/License/license-wimboot.txt b/License/license-wimboot.txt
new file mode 100644 (file)
index 0000000..9179d03
--- /dev/null
@@ -0,0 +1,5 @@
+wimboot follows GPLv2+ license (see gpl-2.0.txt)
+
+Ventoy use the lzx decompress file from wimboot. These code follow the same license as wimboot.
+
+
diff --git a/License/license-xzembedded.txt b/License/license-xzembedded.txt
new file mode 100644 (file)
index 0000000..4641c7f
--- /dev/null
@@ -0,0 +1 @@
+XZ Embedded has been put into the public domain, thus you can do whatever you want with it.
diff --git a/License/license-zlib.txt b/License/license-zlib.txt
new file mode 100644 (file)
index 0000000..cad708d
--- /dev/null
@@ -0,0 +1,27 @@
+/* zlib.h -- interface of the 'zlib' general purpose compression library
+  version 1.2.11, January 15th, 2017
+
+  Copyright (C) 1995-2017 Jean-loup Gailly and Mark Adler
+
+  This software is provided 'as-is', without any express or implied
+  warranty.  In no event will the authors be held liable for any damages
+  arising from the use of this software.
+
+  Permission is granted to anyone to use this software for any purpose,
+  including commercial applications, and to alter it and redistribute it
+  freely, subject to the following restrictions:
+
+  1. The origin of this software must not be misrepresented; you must not
+     claim that you wrote the original software. If you use this software
+     in a product, an acknowledgment in the product documentation would be
+     appreciated but is not required.
+  2. Altered source versions must be plainly marked as such, and must not be
+     misrepresented as being the original software.
+  3. This notice may not be removed or altered from any source distribution.
+
+  Jean-loup Gailly        Mark Adler
+  jloup@gzip.org          madler@alumni.caltech.edu
+
+*/
+
+Ventoy does not modify its source code, only its library is used.
diff --git a/License/license-zstd.txt b/License/license-zstd.txt
new file mode 100644 (file)
index 0000000..f2cad9f
--- /dev/null
@@ -0,0 +1,3 @@
+zstd follows the BSD License (see BSD.txt)
+
+Ventoy does not modify its source code, only its binrary is used.
diff --git a/License/ubdl.txt b/License/ubdl.txt
new file mode 100644 (file)
index 0000000..780ddcd
--- /dev/null
@@ -0,0 +1,59 @@
+UNMODIFIED BINARY DISTRIBUTION LICENCE
+
+
+PREAMBLE
+
+The GNU General Public License provides a legal guarantee that
+software covered by it remains free (in the sense of freedom, not
+price).  It achieves this guarantee by imposing obligations on anyone
+who chooses to distribute the software.
+
+Some of these obligations may be seen as unnecessarily burdensome.  In
+particular, when the source code for the software is already publicly
+and freely available, there is minimal value in imposing upon each
+distributor the obligation to provide the complete source code (or an
+equivalent written offer to provide the complete source code).
+
+This Licence allows for the distribution of unmodified binaries built
+from publicly available source code, without imposing the obligations
+of the GNU General Public License upon anyone who chooses to
+distribute only the unmodified binaries built from that source code.
+
+The extra permissions granted by this Licence apply only to unmodified
+binaries built from source code which has already been made available
+to the public in accordance with the terms of the GNU General Public
+Licence.  Nothing in this Licence allows for the creation of
+closed-source modified versions of the Program.  Any modified versions
+of the Program are subject to the usual terms and conditions of the
+GNU General Public License.
+
+
+TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
+
+This Licence 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 Unmodified Binary Distribution Licence.  All
+terms used in the text of this Licence are to be interpreted as they
+are used in version 2 of the GNU General Public License as published
+by the Free Software Foundation.
+
+If you have made this Program available to the public in both source
+code and executable form in accordance with 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, then you are hereby granted an additional permission to use,
+copy, and distribute the unmodified executable form of this Program
+(the "Unmodified Binary") without restriction, including the right to
+permit persons to whom the Unmodified Binary is furnished to do
+likewise, subject to the following conditions:
+
+- when started running, the Program must display an announcement which
+  includes the details of your existing publication of the Program
+  made in accordance with the terms of the GNU General Public License.
+  For example, the Program could display the URL of the publicly
+  available source code from which the Unmodified Binary was built.
+
+- when exercising your right to grant permissions under this Licence,
+  you do not need to refer directly to the text of this Licence, but
+  you may not grant permissions beyond those granted to you by this
+  Licence.
diff --git a/SQUASHFS/SRC/build_lz4.sh b/SQUASHFS/SRC/build_lz4.sh
new file mode 100644 (file)
index 0000000..883e26e
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+LIBDIR=$PWD/../LIB/LZ4
+
+rm -rf $LIBDIR
+rm -rf lz4-1.8.1.2
+tar -xf lz4-1.8.1.2.tar.gz
+
+
+cd lz4-1.8.1.2
+make && PREFIX=$LIBDIR make install
+
+cd ..
+rm -rf lz4-1.8.1.2
+
+if [ -d $LIBDIR ]; then
+    echo -e "\n========== SUCCESS ============\n"
+else
+    echo -e "\n========== FAILED ============\n"
+fi
+
+
diff --git a/SQUASHFS/SRC/build_lzma.sh b/SQUASHFS/SRC/build_lzma.sh
new file mode 100644 (file)
index 0000000..54f93e0
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/bash
+
+LIBDIR=$PWD/../LIB/LZMA
+
+rm -rf $LIBDIR
+rm -rf liblzma-master
+unzip liblzma-master.zip
+
+cd liblzma-master
+./configure --prefix=$LIBDIR --disable-xz --disable-xzdec --disable-lzmadec --disable-lzmainfo --enable-small
+make -j 8 && make install
+
+cd ..
+rm -rf liblzma-master
+
+if [ -d $LIBDIR ]; then
+    echo -e "\n========== SUCCESS ============\n"
+else
+    echo -e "\n========== FAILED ============\n"
+fi
+
+
diff --git a/SQUASHFS/SRC/build_lzo.sh b/SQUASHFS/SRC/build_lzo.sh
new file mode 100644 (file)
index 0000000..30c1208
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/bash
+
+LIBDIR=$PWD/../LIB/LZO
+LZODIR=lzo-2.08
+
+rm -rf $LIBDIR
+rm -rf $LZODIR
+tar -xf ${LZODIR}.tar.gz
+
+
+cd $LZODIR
+./configure --prefix=$LIBDIR --disable-shared
+
+make && make install
+
+cd ..
+rm -rf $LZODIR
+
+if [ -d $LIBDIR ]; then
+    echo -e "\n========== SUCCESS ============\n"
+else
+    echo -e "\n========== FAILED ============\n"
+fi
+
diff --git a/SQUASHFS/SRC/build_zstd.sh b/SQUASHFS/SRC/build_zstd.sh
new file mode 100644 (file)
index 0000000..aea55bb
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/bash
+
+LIBDIR=$PWD/../LIB/ZSTD
+ZSTDDIR=zstd-1.4.4
+
+rm -rf $LIBDIR
+rm -rf $ZSTDDIR
+tar -xf ${ZSTDDIR}.tar.gz
+
+
+cd $ZSTDDIR
+PREFIX=$LIBDIR ZSTD_LIB_COMPRESSION=0 make
+PREFIX=$LIBDIR ZSTD_LIB_COMPRESSION=0 make install
+
+cd ..
+rm -rf $ZSTDDIR
+
+if [ -d $LIBDIR ]; then
+    echo -e "\n========== SUCCESS ============\n"
+else
+    echo -e "\n========== FAILED ============\n"
+fi
+
diff --git a/SQUASHFS/SRC/liblzma-master.zip b/SQUASHFS/SRC/liblzma-master.zip
new file mode 100644 (file)
index 0000000..5528ab8
Binary files /dev/null and b/SQUASHFS/SRC/liblzma-master.zip differ
diff --git a/SQUASHFS/SRC/lz4-1.8.1.2.tar.gz b/SQUASHFS/SRC/lz4-1.8.1.2.tar.gz
new file mode 100644 (file)
index 0000000..6aac7ad
Binary files /dev/null and b/SQUASHFS/SRC/lz4-1.8.1.2.tar.gz differ
diff --git a/SQUASHFS/SRC/lzo-2.08.tar.gz b/SQUASHFS/SRC/lzo-2.08.tar.gz
new file mode 100644 (file)
index 0000000..1854070
Binary files /dev/null and b/SQUASHFS/SRC/lzo-2.08.tar.gz differ
diff --git a/SQUASHFS/SRC/zstd-1.4.4.tar.gz b/SQUASHFS/SRC/zstd-1.4.4.tar.gz
new file mode 100644 (file)
index 0000000..96e22ad
Binary files /dev/null and b/SQUASHFS/SRC/zstd-1.4.4.tar.gz differ
diff --git a/SQUASHFS/squashfs-tools-4.4/ACKNOWLEDGEMENTS b/SQUASHFS/squashfs-tools-4.4/ACKNOWLEDGEMENTS
new file mode 100644 (file)
index 0000000..2097bb7
--- /dev/null
@@ -0,0 +1,162 @@
+                       ACKNOWLEDGEMENTS
+
+Thanks to everyone who have downloaded Squashfs.  I appreciate people
+using it, and any feedback you have.
+
+The following have provided useful feedback, which has guided
+some of the extra features in squashfs.  This is a randomly ordered
+(roughly in chronological order) list, which is updated when
+I remember...
+
+Acknowledgements for Squashfs 4.3
+---------------------------------
+
+Thanks to Bruno Wolff III and Andy Lutomirski for useful feedback
+during the long development process of Squashfs 4.3.
+
+Acknowledgements for Squashfs 4.2
+---------------------------------
+
+Thanks to Lasse Collin (http://tukaani.org/xz/) for mainlining XZ
+decompression support.
+
+Acknowledgements for Squashfs 4.1
+---------------------------------
+
+Thanks to Chan Jeong <chan.jeong@lge.com> and LG for the patches to support LZO
+compression.
+
+Acknowledgements for Squashfs 4.0
+---------------------------------
+
+Thanks to Tim Bird and CELF (Consumer Electronics Linux Forum) for helping
+fund mainstreaming of Squashfs into the 2.6.29 kernel and the 
+changes to the Squashfs tools to support the new 4.0 file system layout.
+
+Acknowledgements for Squashfs-3.3
+------------------------------------
+
+Peter Korsgaard and others sent patches updating Squashfs to changes in the
+VFS interface for 2.6.22/2.6.23/2.6.24-rc1.  Peter also sent some small patches
+for the Squashfs kernel code.
+
+Vito Di Leo sent a patch extending Mksquashfs to support regex filters.
+While his patched worked, it unfortunately made it easy to make Mksquashfs
+perform unpredictably with poorly choosen regex expressions.  It, however,
+encouraged myself to add support for wildcard pattern matching and regex
+filters in a different way.
+
+Acknowledgements for Squashfs-3.2-r2
+------------------------------------
+
+Junjiro Okajima discovered a couple of SMP issues, thanks.
+
+Junjiro Okajima and Tomas Matejicek have produced some good LZMA patches
+for Squashfs.
+
+Acknowledgements for Squashfs-3.2
+---------------------------------
+
+Peter Korsgaard sent a patch updating Squashfs to changes in the VFS interface
+in Linux 2.6.20.
+
+Acknowledgements for Squashfs-3.1
+---------------------------------
+
+Kenneth Duda and Ed Swierk of Arastra Inc. identified numerous bugs with
+Squashfs, and provided patches which were the basis for some of the
+fixes.  In particular they identified the fragment rounding bug, the
+NFS bug, the initrd bug, and helped identify the 4K stack overflow bug.
+
+Scott James Remnant (Ubuntu) also identified the fragment rounding bug,
+and he also provided a patch.
+
+Ming Zhang identified the Lseek bug in Mksquashfs.  His tests on the
+performance of Mksquashfs on SMP systems encouraged the rewrite of
+Mksquashfs.
+
+Peter Korsgaard, Daniel Olivera and Zilvinas Valinskas noticed
+Squashfs 3.0 didn't compile on Linux-2.6.18-rc[1-4] due to changes
+in the Linux VFS interfaces, and provided patches.
+
+Tomas Matejicek (SLAX) suggested the -force option on Unsquashfs, and noticed
+Unsquashfs didn't return the correct exit status.
+
+Yann Le Doare reported a kernel oops and provided a Qemu image that led
+to the identification of the simultaneously accessing multiply mounted Squashfs
+filesystems bug.
+
+
+Older acknowledgements
+----------------------
+
+Mark Robson - pointed out early on that initrds didn't work
+
+Adam Warner - pointed out that greater than 2GB filesystems didn't work.
+
+John Sutton - raised the problem when archiving the entire filesystem
+(/) there was no way to prevent /proc being archived.  This prompted
+exclude files.
+
+Martin Mueller (LinuxTV) - noticed that the filesystem length in the
+superblock doesn't match the output filesystem length.  This is due to
+padding to a 4K boundary.  This prompted the addition of the -nopad option.
+He also reported a problem where 32K block filesystems hung when used as
+initrds.
+
+Arkadiusz Patyk (Polish Linux Distribution - PLD) reported a problem where 32K
+block filesystems hung when used as a root filesystem mounted as a loopback
+device.
+
+David Fox (Lindows) noticed that the exit codes returned by Mksquashfs were
+wrong.  He also noticed that a lot of time was spent in the duplicate scan
+routine.
+
+Cameron Rich complained that Squashfs did not support FIFOs or sockets.
+
+Steve Chadsey and Thomas Weissmuller noticed that files larger than the
+available memory could not be compressed by Mksquashfs.
+
+"Ptwahyu" and "Hoan" (I have no full names and I don't like giving people's
+email addresses), noticed that Mksquashfs 1.3 SEGV'd occasionally.  Even though
+I had already noticed this bug, it is useful to be informed by other people.
+
+Don Elwell, Murray Jensen and Cameron Rich, have all sent in patches.  Thanks,
+I have not had time to do anything about them yet...
+
+Drew Scott Daniels has been a good advocate for Squashfs.
+
+Erik Andersen has made some nice suggestions, unfortunately, I have
+not had time to implement anything.
+
+Artemiy I. Pavlov has written a useful LDP mini-howto for Squashfs
+(http://linuxdoc.artemio.net/squashfs).
+
+Yves Combe reported the Apple G5 bug, when using Squashfs for
+his PPC Knoppix-mib livecd project.
+
+Jaco Greeff (mklivecd project, and maintainer of the Mandrake
+squashfs-tools package) suggested the new mksquashfs -ef option, and the
+standalone build for mksquashfs.
+
+Mike Schaudies made a donation.
+
+Arkadiusz Patyk from the Polish Linux Distribution reported that Squashfs
+didn't work on amd64 machines. He gave me an account on a PLD amd64 machine
+which allowed myself to track down these bugs.
+
+Miles Roper, Peter Kjellerstedt and Willy Tarreau reported that release 2.1 did
+not compile with gcc < 3.x.
+
+Marcel J.E. Mol reported lack of kernel memory issues when using Squashfs
+on small memory embedded systems.  This prompted the addition of the embedded
+system kernel configuration options.
+
+Era Scarecrow noticed that Mksquashfs had not been updated to reflect that
+smaller than 4K blocks are no longer supported.
+
+Kenichi Shima reported the Kconfig file had not been updated to 2.2.
+
+Aaron Ten Clay made a donation!
+
+Tomas Matejicek (SLAX) made a donation!
diff --git a/SQUASHFS/squashfs-tools-4.4/CHANGES b/SQUASHFS/squashfs-tools-4.4/CHANGES
new file mode 100644 (file)
index 0000000..c35107b
--- /dev/null
@@ -0,0 +1,664 @@
+                       SQUASHFS CHANGE LOG
+
+4.4    29 AUG 2019     Reproducible builds, new compressors,
+                       CVE fixes, security hardening and new options
+                       for Mksquashfs/Unsquashfs.
+
+       1. Overall improvements:
+
+               1.1 Mksquashfs now generates reproducible images by default.
+               1.2 Mkfs time and file timestamps can also be specified.
+               1.3 Support for the Zstandard (ZSTD) compression algorithm.
+               1.4 CVE-2015-4645 and CVE-2015-4646 have been fixed.
+
+       2. Mksquashfs improvements and major bug fixes:
+
+               2.1 Pseudo files now support symbolic links.
+               2.2 New -mkfs-time option.
+               2.3 New -all-time option.
+               2.4 New -root-mode option.
+               2.5 New -quiet option.
+               2.6 New -noId option.
+               2.7 New -offset option.
+               2.8 Update lz4 wrapper to use new functions introduced
+                   in 1.7.0.
+               2.9 Bug fix, don't allow "/" pseudo filenames.
+               2.10 Bug fix, allow quoting of pseudo files, to
+                    better handle filenames with spaces.
+               2.11 Fix compilation with glibc 2.25+.
+
+       3. Unsquashfs improvements and major bug fixes:
+
+               3.1 CVE-2015-4645 and CVE-2015-4646 have been fixed.
+               3.2 Unsquashfs has been further hardened against corrupted
+                   filestems.
+               3.3 Unsquashfs is now more strict about error handling.
+               3.4 New -ignore-errors option.
+               3.5 New -strict-errors option.
+               3.6 New -lln[umeric] option.
+               3.7 New -lc option.
+               3.8 New -llc option.
+               3.9 New -mkfs-time option.
+               3.10 New -UTC option.
+               3.11 New -offset option.
+               3.12 New -quiet option.
+               3.13 Update lz4 wrapper to use new functions introduced
+                    in 1.7.0.
+               3.14 Bug fix, fatal and non-fatal errors now set the exit
+                    code to 1.
+               3.15 Bug fix, fix time setting for symlinks.
+               3.16 Bug fix, try to set sticky-bit when running as a
+                    user process.
+               3.17 Fix compilation with glibc 2.25+.
+
+4.3    12 MAY 2014     New compressor options, new Mksquashfs/Unsquashfs
+                       functionality, duplicate checking optimisations,
+                       stability improvements (option/file parsing,
+                       buffer/memory overflow checks, filesystem hardening
+                       on corrupted filesystems), CVE fixes.
+
+       Too many changes to do the traditional custom changelog.  But, this
+       is now unnecessary, so instead list most significant 15% of commits
+       from git changelog in chronological order.
+
+       - unsquashfs: add checks for corrupted data in opendir functions
+       - unsquashfs: completely empty filesystems incorrectly generate an error
+       - unsquashfs: fix open file limit
+       - mksquashfs: Use linked list to store directory entries rather
+       - mksquashfs: Remove qsort and add a bottom up linked list merge sort
+       - mksquashfs: optimise lookup_inode2() for dirs
+       - pseudo: fix handling of modify pseudo files
+       - pseudo: fix handling of directory pseudo files
+       - xattr: Fix ERROR() so that it is synchronised with the progress bar
+       - mksquashfs/sort: Fix INFO() so that it is synced with the progress bar
+       - mksquashfs: Add -progress to force progress bar when using -info
+       - error.h: consolidate the various error macros into one header file
+       - mksquashfs: fix stack overflow in write_fragment_table()
+       - mksquashfs: move list allocation from off the stack
+       - unsquashfs: fix oversight in directory permission setting
+       - mksquashfs: dynamically allocate recovery_file
+       - mksquashfs: dynamically allocate buffer in subpathname()
+       - mksquashfs: dynamically allocate buffer in pathname()
+       - unsquashfs: fix CVE-2012-4024
+       - unsquashfs: fix CVE-2012-4025
+       - mksquashfs: fix potential stack overflow in get_component()
+       - mksquashfs: add parse_number() helper for numeric command line options
+       - mksquasfs: check return value of fstat() in reader_read_file()
+       - mksquashfs: dynamically allocate filename in old_add_exclude()
+       - unsquashfs: dynamically allocate pathname in dir_scan()
+       - unsquashfs: dynamically allocate pathname in pre_scan()
+       - sort: dynamically allocate filename in add_sort_list()
+       - mksquashfs: fix dir_scan() exit if lstat of source directory fails
+       - pseudo: fix memory leak in read_pseudo_def() if exec_file() fails
+       - pseudo: dynamically allocate path in dump_pseudo()
+       - mksquashfs: dynamically allocate path in display_path2()
+       - mksquashfs: dynamically allocate b_buffer in getbase()
+       - pseudo: fix potential stack overflow in get_component()
+       - pseudo: avoid buffer overflow in read_pseudo_def() using sscanf()
+       - pseudo: dynamically allocate filename in exec_file()
+       - pseudo: avoid buffer overflow in read_sort_file() using fscanf()
+       - sort: tighten up sort file parsing
+       - unsquashfs: fix name under-allocation in process_extract_files()
+       - unsquashfs: avoid buffer overflow in print_filename() using sprintf()
+       - Fix some limits in the file parsing routines
+       - pseudo: Rewrite pseudo file processing
+       - read_fs: fix small memory leaks in read_filesystem()
+       - mksquashfs: fix fclose leak in reader_read_file() on I/O error
+       - mksquashfs: fix frag struct leak in write_file_{process|blocks|frag}
+       - unsquashfs_xattr: fix memory leak in write_xattr()
+       - read_xattrs: fix xattr free in get_xattr() in error path
+       - unsquashfs: add -user-xattrs option to only extract user.xxx xattrs
+       - unsquashfs: add code to only print "not superuser" error message once
+       - unsquashfs: check for integer overflow in user input
+       - mksquashfs: check for integer overflow in user input
+       - mksquashfs: fix "new" variable leak in dir_scan1()
+       - read_fs: prevent buffer {over|under}flow in read_block() with
+         corrupted filesystems
+       - read_fs: check metadata blocks are expected size in scan_inode_table()
+       - read_fs: check the root inode block is found in scan_inode_table()
+       - read_fs: Further harden scan_inode_table() against corrupted
+         filesystems
+       - unsquashfs: prevent buffer {over|under}flow in read_block() with
+         corrupted filesystems
+       - read_xattrs: harden xattr data reading against corrupted filesystems
+       - unsquash-[23]: harden frag table reading against corrupted filesystems
+       - unsquash-4.c: harden uid/gid & frag table reading against corruption
+       - unsquashfs: harden inode/directory table reading against corruption
+       - mksquashfs: improve out of space in output filesystem handling
+       - mksquashfs: flag lseek error in writer as probable out of space
+       - mksquashfs: flag lseek error in write_destination as probable out of
+         space
+       - mksquashfs: print file being squashed when ^\ (SIGQUIT) typed
+       - mksquashfs: make EXIT_MKSQUASHFS() etc restore via new restore thread
+       - mksquashfs: fix recursive restore failure check
+       - info: dump queue and cache status if ^\ hit twice within one second
+       - mksquashfs: fix rare race condition in "locked fragment" queueing
+       - lz4: add experimental support for lz4 compression
+       - lz4: add support for lz4 "high compression"
+       - lzo_wrapper: new implementation with compression options
+       - gzip_wrapper: add compression options
+       - mksquashfs: redo -comp <compressor> parsing
+       - mksquashfs: display compressor options when -X option isn't recognised
+       - mksquashfs: add -Xhelp option
+       - mksquashfs/unsquashfs: fix mtime signedness
+       - Mksquashfs: optimise duplicate checking when appending
+       - Mksquashfs: introduce additional per CPU fragment process threads
+       - Mksquashfs: significantly optimise fragment duplicate checking
+       - read_fs: scan_inode_table(), fix memory leak on filesystem corruption
+       - pseudo: add_pseudo(), fix use of freed variable
+       - mksquashfs/unsquashfs: exclude/extract/pseudo files, fix handling of
+         leaf name
+       - mksquashfs: rewrite default queue size so it's based on physical mem
+       - mksquashfs: add a new -mem <mbytes> option
+       - mksquashfs: fix limit on the number of dynamic pseudo files
+       - mksquashfs: make -mem take a normal byte value, optionally with a
+         K, M or G 
+
+4.2    28 FEB 2011     XZ compression, and compression options support
+
+       1. Filesystem improvements:
+
+           1.1 Added XZ compression
+           1.2 Added compression options support
+
+       2. Miscellaneous improvements/bug fixes
+
+           1.1 Add missing NO_XATTR filesystem flag to indicate no-xattrs
+               option was specified and no xattrs should be stored when
+               appending.
+           1.2 Add suppport in Unquashfs -stat option for displaying
+               NO_XATTR flag.
+           1.3 Remove checkdata entry from Unsquashfs -stat option if a 4.0
+               filesystem - checkdata is no longer supported.
+           1.4 Fix appending bug when appending to an empty filesystem - this
+               would be incorrectly treated as an error.
+           1.5 Use glibc sys/xattr.h include rather than using attr/xattr.h
+               which isn't present by default on some distributions.
+           1.6 Unsquashfs, fix block calculation error with regular files when
+               file size is between 2^32-block_size+1 and 2^32-1.
+           1.7 Unsquashfs, fix sparse file writing when holes are larger than
+               2^31-1.
+           1.8 Add external CFLAGS and LDFLAGS support to Makefile, and allow
+               build options to be specified on command line.  Also don't
+               over-write passed in CFLAGS definition.
+       
+
+4.1    19 SEPT 2010    Major filesystem and tools improvements
+
+       1. Filesystem improvements:
+
+           1.1 Extended attribute support
+           1.2 New compression framework
+           1.3 Support for LZO compression
+           1.4 Support for LZMA compression (not yet in mainline)
+
+       2. Mksquashfs improvements:
+
+           1.1 Enhanced pseudo file support
+           1.2 New options for choosing compression algorithm used
+           1.3 New options for controlling extended attributes
+           1.4 Fix misalignment issues with memcpy etc. seen on ARM
+           1.5 Fix floating point error in progress_bar when max == 0
+           1.6 Removed use of get_nproc() call unavailable in ulibc
+           1.7 Reorganised help text
+         
+       3. Unsquashfs improvements:
+
+           1.1 New options for controlling extended attributes
+           1.2 Fix misalignment issues with memcpy etc. seen on ARM
+           1.3 Fix floating point error in progress_bar when max == 0
+           1.4 Removed use of get_nproc() call unavailable in ulibc
+
+         
+4.0    5 APR 2009      Major filesystems improvements
+
+       1. Kernel code improvements:
+
+           1.1 Fixed little endian layout adopted.  All swapping macros
+               removed, and in-line swapping added for big-endian
+               architectures.
+           1.2 Kernel code substantially improved and restructured.
+           1.3 Kernel code split into separate files along functional lines.
+           1.4 Vmalloc usage removed, and code changed to use separately
+               allocated 4K buffers
+
+       2. Unsquashfs improvements:
+
+           2.1 Support for 4.0 filesystems added.
+           2.2 Swapping macros rewritten.
+           2.3 Unsquashfs code restructured and split into separate files.
+
+       3. Mksquashfs improvements:
+
+           3.1 Swapping macros rewritten.  Fixed little-endian layout allows
+               code to be optimised and only added at compile time for
+               big endian systems.
+           3.2 Support for pseudo files added.
+        
+3.4    26 AUG 2008     Performance improvements to Unsquashfs, Mksquashfs
+                       and the kernel code.  Plus many small bug fixes.
+
+       1. Kernel code improvements:
+
+           1.1 Internal Squashfs kernel metadata and fragment cache
+               implementations have been merged and optimised.  Spinlocks are
+               now used, locks are held for smaller periods and wakeups have
+               been minimised.  Small race condition fixed where if two or
+               more processes tried to read the same cache block
+               simultaneously they would both read and decompress it.  10-20%+
+               speed improvement has been seen on tests.
+           1.2 NFS export code rewritten following VFS changes in
+               linux-2.6.24.
+           1.3 New patches for linux-2.6.25, linux-2.6.26, and linux-2.6.27.
+               Fixed patch for linux-2.6.24.
+           1.4 Fixed small buffer_head leak in squashfs_read_data when
+               handling badly corrupted filesystems.
+           1.5 Fixed bug in get_dir_index_using_offset.
+
+       2. Unsquashfs improvements:
+
+           2.1 Unsquashfs has been parallelised.  Filesystem reading, writing
+               and decompression is now multi-threaded.  Up to 40% speed
+               improvement seen on tests.
+           2.2 Unsquashfs now has a progress bar.  Use -no-progress to
+               disable it.
+           2.3 Fixed small bug where unistd.h wasn't being included on
+               some distributions, leading to lseek being used rather than
+               lseek64 - which meant on these distributions Unsquashfs
+               couldn't unsquash filesystems larger than 4GB.
+
+       3. Mksquashfs improvements:
+
+           3.1 Removed some small remaining parallelisation bottlenecks.
+               Depending on source filesystem, up to 10%+ speed improvement.
+           3.2 Progress bar improved, and moved to separate thread.
+           3.3 Sparse file handling bug in Mksquashfs 3.3 fixed.
+           3.4 Two rare appending restore bugs fixed (when ^C hit twice).
+
+
+3.3    1 NOV 2007      Increase in block size, sparse file support,
+                       Mksquashfs and Unsquashfs extended to use
+                       pattern matching in exclude/extract files, plus
+                       many more improvements and bug fixes.
+
+       1. Filesystem improvements:
+
+            1.1. Maximum block size has been increased to 1Mbyte, and the
+                 default block size has been increased to 128 Kbytes.
+                 This improves compression.
+
+            1.2. Sparse files are now supported.  Sparse files are files
+                 which have large areas of unallocated data commonly called
+                 holes.  These files are now detected by Squashfs and stored
+                 more efficiently.  This improves compression and read
+                 performance for sparse files.
+
+       2. Mksquashfs improvements:
+
+          2.1.  Exclude files have been extended to use wildcard pattern
+                matching and regular expressions.  Support has also been
+                added for non-anchored excludes, which means it is
+                now possible to specify excludes which match anywhere
+                in the filesystem (i.e. leaf files), rather than always
+                having to specify exclude files starting from the root
+                directory (anchored excludes).
+
+          2.2.  Recovery files are now created when appending to existing
+                Squashfs filesystems.  This allows the original filesystem
+                to be recovered if Mksquashfs aborts unexpectedly
+                (i.e. power failure).
+
+       3. Unsquashfs improvements:
+
+           3.1. Multiple extract files can now be specified on the
+                command line, and the files/directories to be extracted can
+                now also be given in a file.
+
+           3.2. Extract files have been extended to use wildcard pattern
+                matching and regular expressions.
+
+           3.3. Filename printing has been enhanced and Unquashfs can
+                now display filenames with file attributes
+                ('ls -l' style output).
+
+           3.4. A -stat option has been added which displays the filesystem
+                superblock information.
+
+           3.5. Unsquashfs now supports 1.x filesystems.
+
+       4. Miscellaneous improvements/bug fixes:
+
+           4.1. Squashfs kernel code improved to use SetPageError in
+                squashfs_readpage() if I/O error occurs.
+
+           4.2. Fixed Squashfs kernel code bug preventing file
+                seeking beyond 2GB.
+
+           4.3. Mksquashfs now detects file size changes between
+                first phase directory scan and second phase filesystem create.
+                It also deals better with file I/O errors.
+
+
+3.2-r2 15 JAN 2007     Kernel patch update and progress bar bug fix
+
+       1. Kernel patches 2.6.19/2.6.20 have been updated to use
+          const structures and mutexes rather than older semaphores.
+       2. Minor SMP bug fixes.
+       3. Progress bar broken on x86-64.  Fixed.
+
+3.2    2 JAN 2007      NFS support, improvements to the Squashfs-tools, major
+                       bug fixes, lots of small improvements/bug fixes, and new
+                       kernel patches.
+
+       Improvements:
+
+       1. Squashfs filesystems can now be exported via NFS.
+       2. Unsquashfs now supports 2.x filesystems.
+       3. Mksquashfs now displays a progress bar.
+       4. Squashfs kernel code has been hardened against accidently or
+          maliciously corrupted Squashfs filesystems.
+
+       Bug fixes:
+
+       5. Race condition occurring on S390 in readpage() fixed.
+       6. Odd behaviour of MIPS memcpy in read_data() routine worked-around.
+       7. Missing cache_flush in Squashfs symlink_readpage() added.
+       
+
+3.1-r2 30 AUG 2006     Mksquashfs -sort bug fix
+
+                       A code optimisation after testing unfortunately
+                       broke sorting in Mksquashfs.  This has been fixed.
+
+3.1    19 AUG 2006     This release has some major improvements to
+                       the squashfs-tools, a couple of major bug
+                       fixes, lots of small improvements/bug fixes,
+                       and new kernel patches.
+
+                       
+       1. Mksquashfs has been rewritten to be multi-threaded.  It
+          has the following improvements
+
+          1.1. Parallel compression.  By default as many compression and
+               fragment compression threads are created as there are available
+               processors.  This significantly speeds up performance on SMP
+               systems.
+          1.2. File input and filesystem output is peformed in parallel on
+               separate threads to maximise I/O performance.  Even on single
+               processor systems this speeds up performance by at least 10%.
+          1.3. Appending has been significantly improved, and files within the
+               filesystem being appended to are no longer scanned and
+               checksummed.  This significantly improves append time for large
+               filesystems.
+          1.4. File duplicate checking has been optimised, and split into two
+               separate phases.  Only files which are considered possible
+               duplicates after the first phase are checksummed and cached in
+               memory.
+          1.5  The use of swap memory was found to significantly impact
+               performance. The amount of memory used to cache files is now a
+               command line option, by default this is 512 Mbytes.
+       2. Unsquashfs has the following improvements
+
+          2.1  Unsquashfs now allows you to specify the filename or the
+               directory within the Squashfs filesystem that is to be
+               extracted, rather than always extracting the entire filesystem.
+          2.2  A new -force option has been added which forces Unsquashfs to
+               output to the destination directory even if files and directories
+               already exist in the destination directory.  This allows you to
+               update an already existing directory tree, or to Unsquashfs to
+               a partially filled directory tree.  Without the -force option
+               Unsquashfs will refuse to output.
+
+       3.  The following major bug fixes have been made
+
+         3.1   A fragment table rounding bug has been fixed in Mksquashfs.
+               Previously if the number of fragments in the filesystem
+               were a multiple of 512, Mksquashfs would generate an
+               incorrect filesystem.
+         3.2   A rare SMP bug which occurred when simultaneously acccessing
+               multiply mounted Squashfs filesystems has been fixed.
+
+       4. Miscellaneous improvements/bug fixes
+
+         4.1   Kernel code stack usage has been reduced.  This is to ensure
+               Squashfs works with 4K stacks.
+         4.2   Readdir (Squashfs kernel code) has been fixed to always
+               return 0, rather than the number of directories read.  Squashfs
+               should now interact better with NFS.
+         4.3   Lseek bug in Mksquashfs when appending to larger than 4GB
+               filesystems fixed.
+         4.4   Squashfs 2.x initrds can now been mounted.
+         4.5   Unsquashfs exit status fixed.
+         4.6   New patches for linux-2.6.18 and linux-2.4.33.
+
+       
+3.0    15 MAR 2006     Major filesystem improvements
+
+       1. Filesystems are no longer limited to 4 GB.  In
+          theory 2^64 or 4 exabytes is now supported.
+       2. Files are no longer limited to 4 GB.  In theory the maximum
+          file size is 4 exabytes.
+       3. Metadata (inode table and directory tables) are no longer
+          restricted to 16 Mbytes.
+       4. Hardlinks are now suppported.
+       5. Nlink counts are now supported.
+       6. Readdir now returns '.' and '..' entries.
+       7. Special support for files larger than 256 MB has been added to
+          the Squashfs kernel code for faster read access.
+       8. Inode numbers are now stored within the inode rather than being
+          computed from inode location on disk (this is not so much an
+          improvement, but a change forced by the previously listed
+          improvements).
+
+2.2-r2 8 SEPT 2005     Second release of 2.2, this release fixes a couple
+                       of small bugs, a couple of small documentation
+                       mistakes, and adds a patch for kernel 2.6.13. 
+
+       1. Mksquashfs now deletes the output filesystem image file if an
+          error occurs whilst generating the filesystem.  Previously on
+          error the image file was left empty or partially written.
+       2. Updated mksquashfs so that it doesn't allow you to generate
+          filesystems with block sizes smaller than 4K.  Squashfs hasn't
+          supported block sizes less than 4K since 2.0-alpha.
+       3. Mksquashfs now ignores missing files/directories in sort files.
+          This was the original behaviour before 2.2.
+       4. Fixed small mistake in fs/Kconfig where the version was still
+          listed as 2.0.
+       5. Updated ACKNOWLEDGEMENTS file.
+
+
+2.2    3 JUL 2005      This release has some small improvements, bug fixes
+                       and patches for new kernels.
+
+       1. Sort routine re-worked and debugged from release 2.1.  It now allows
+          you to give Mkisofs style sort files and checks for filenames that
+          don't match anything.  Sort priority has also been changed to
+          conform to Mkisofs usage, highest priority files are now placed
+          at the start of the filesystem (this means they will be on the
+          inside of a CD or DVD).
+       2. New Configure options for embedded systems (memory constrained
+          systems).  See INSTALL file for further details.
+       3. Directory index bug fixed where chars were treated as signed on
+           some architectures.  A file would not be found in the rare case
+          that the filename started with a chracter greater than 127.
+       4. Bug introduced into the read_data() routine when sped up to use data
+          block queueing fixed.  If the second or later block resulted in an
+          I/O error this was not checked.
+       5. Append bug introduced in 2.1 fixed.  The code to compute the new
+          compressed and uncompressed directory parts after appending was
+          wrong.
+       6. Metadata block length read routine altered to not perform a
+          misaligned short read.  This was to fix reading on an ARM7 running
+          uCLinux without a misaligned read interrupt handler.
+       7. Checkdata bug introduced in 2.1 fixed.
+       
+
+2.1-r2  15 DEC 2004    Code changed so it can be compiled with gcc 2.x
+
+       1.  In some of the code added for release 2.1 I unknowingly used some
+           gcc extensions only supported by 3.x compilers.  I have received
+           a couple of reports that the 2.1 release doesn't build on 2.x and so
+           people are clearly still using gcc 2.x.  The code has been
+           rewritten to remove these extensions.
+
+2.1    10 DEC 2004     Significantly improved directory handling plus numerous
+                       other smaller improvements
+
+       1.  Fast indexed directories implemented.  These speed up directory
+           operations (ls, file lookup etc.) significantly for directories
+           larger than 8 KB.
+       2.  All directories are now sorted in alphabetical order.  This again
+           speeds up directory operations, and in some cases it also results in
+           a small compression improvement (greater data similarity between
+           files with alphabetically similar names).
+       3.  Maximum directory size increased from 512 KB to 128 MB.
+       4.  Duplicate fragment checking and appending optimised in mksquashfs,
+           depending on filesystem, this is now up to 25% faster.
+       5.  Mksquashfs help information reformatted and reorganised.
+       6.  The Squashfs version and release date is now printed at kernel
+           boot-time or module insertion.  This addition will hopefully help
+           to reduce the growing problem where the Squashfs version supported
+           by a kernel is unknown and the kernel source is unavailable.
+        7.  New PERFORMANCE.README file.
+       8.  New -2.0 mksquashfs option.
+       9.  CHANGES file reorganised.
+       10. README file reorganised, clarified and updated to include the 2.0
+           mksquashfs options.
+       11. New patch for Linux 2.6.9.
+       12. New patch for Linux 2.4.28.
+
+2.0r2  29 AUG 2004     Workaround for kernel bug in kernels 2.6.8 and newer
+                       added
+
+       1. New patch for kernel 2.6.8.1.  This includes a workaround for a
+          kernel bug introduced in 2.6.7bk14, which is present in all later
+          versions of the kernel.  
+
+          If you're using a 2.6.8 kernel or later then you must use this
+          2.6.8.1 patch.  If you've experienced hangs or oopses using Squashfs
+          with a 2.6.8 or later kernel then you've hit this bug, and this
+          patch will fix it.
+
+          It is worth mentioning that this kernel bug potentially affects
+          other filesystems.  If you receive odd results with other
+          filesystems you may be experiencing this bug with that filesystem.
+          I submitted a patch but this has not yet gone into the
+          kernel, hopefully the bug will be fixed in later kernels. 
+
+2.0    13 JULY 2004    A couple of new options, and some bug fixes
+
+       1. New mksquashfs -all-root, -root-owned, -force-uid, and -force-gid
+          options.  These allow the uids/gids of files in the generated
+          filesystem to be specified, overriding the uids/gids in the
+          source filesystem.
+       2. Initrds are now supported for kernels 2.6.x.
+       3. amd64 bug fixes.  If you use an amd64, please read the README-AMD64
+          file.
+       4. Check-data and gid bug fixes.  With 2.0-alpha when mounting 1.x
+          filesystems in certain cases file gids were corrupted.
+       5. New patch for Linux 2.6.7.
+
+2.0-ALPHA      21 MAY 2004     Filesystem changes and compression improvements
+
+       1. Squashfs 2.0 has added the concept of fragment blocks.
+           Files smaller than the file block size and optionally the
+          remainder of files that do not fit fully into a block (i.e. the
+          last 32K in a 96K file) are packed into shared fragments and
+          compressed together.  This achieves on average 5 - 20% better
+          compression than Squashfs 1.x.
+       2. The maximum block size has been increased to 64K (in the ALPHA
+          version of Squashfs 2.0).
+       3. The maximum number of UIDs has been increased to 256 (from 48 in
+          1.x).
+       4. The maximum number of GIDs has been increased to 256 (from 15 in
+          1.x).
+       5. Removal of sleep_on() function call in 2.6.x patch, to allow Squashfs
+          to work on the Fedora rc2 kernel.
+       6. Numerous small bug fixes have been made.
+
+1.3r3  18 JAN 2004     Third release of 1.3, this adds a new mksquashfs option,
+                       some bug fixes, and extra patches for new kernels
+
+       1. New mksquashfs -ef exclude option.  This option reads the exclude
+          dirs/files from an exclude file, one exclude dir/file per line.  This
+          avoids the command line size limit when using the -e exclude option,
+       2. When appending to existing filesystems, if mksquashfs experiences a
+          fatal error (e.g. out of space when adding to the destination), the
+          original filesystem is restored,
+       3. Mksquashfs now builds standalone, without the kernel needing to be
+          patched.
+       4. Bug fix in the kernel squashfs filesystem, where the pages being
+          filled were not kmapped.  This seems to only have caused problems
+          on an Apple G5,
+       5. New patch for Linux 2.4.24,
+
+       6. New patch for Linux 2.6.1, this replaces the patch for 2.6.0-test7.
+
+1.3r2  14 OCT 2003     Second release of 1.3, bug fixes and extra patches for
+                       new kernels
+
+       1. Bug fix in routine that adds files to the filesystem being
+          generated in mksquashfs.  This bug was introduced in 1.3
+          (not enough testing...) when I rewrote it to handle files larger
+          than available memory.  This bug caused a SEGV, so if you've ever
+          got that, it is now fixed,
+       2. Long running bug where ls -s and du reported wrong block size
+          fixed.  I'm pretty sure this used to work many kernel versions ago
+          (2.4.7) but it broke somewhere along the line since then,
+       3. New patch for Linux 2.4.22,
+       4. New patch for 2.6.0-test7, this replaces the patch for 2.6.0-test1.
+
+1.3    29 JUL 2003     FIFO/Socket support added plus optimisations and
+                       improvements
+
+       1. FIFOs and Socket inodes are now supported,
+       2. Mksquashfs can now compress files larger than available
+          memory,
+       3. File duplicate check routine optimised,
+       4. Exit codes fixed in Mksquashfs,
+       5. Patch for Linux 2.4.21,
+       6. Patch for Linux 2.6.0-test1.  Hopefully, this will work for
+          the next few releases of 2.6.0-testx, otherwise, I'll be
+          releasing a lot of updates to the 2.6.0 patch...
+
+1.2    13 MAR 2003     Append feature and new mksquashfs options added
+
+       Mksquashfs can now add to existing squashfs filesystems.  Three extra
+       options "-noappend", "-keep-as-directory", and "root-becomes"
+       have been added.
+
+       The append option with file duplicate detection, means squashfs can be
+       used as a simple versioning archiving filesystem. A squashfs
+       filesystem can be created with for example the linux-2.4.19 source.
+       Appending the linux-2.4.20 source will create a filesystem with the
+       two source trees, but only the changed files will take extra room,
+       the unchanged files will be detected as duplicates.
+
+       See the README file for usage changes.
+
+1.1b   16 JAN 2003     Bug fix release
+
+       Fixed readpage deadlock bug.  This was a rare deadlock bug that
+       happened when pushing pages into the page cache when using greater
+       than 4K blocks.  I never got this bug when I tested the filesystem,
+       but two people emailed me on the same day about the problem!
+       I fixed it by using a page cache function that wasn't there when
+       I originally did the work, which was nice :-)
+
+1.1    8 JAN 2003      Added features
+
+       1. Kernel squashfs can now mount different byte order filesystems.
+       2. Additional features added to mksquashfs.  Mksquashfs now supports
+          exclude files and multiple source files/directories can be
+          specified.  A nopad option has also been added, which
+          informs mksquashfs not to pad filesystems to a multiple of 4K.
+          See README for mksquashfs usage changes.
+       3. Greater than 2GB filesystems bug fix.  Filesystems greater than 2GB
+          can now be created.
+
+1.0c   14 NOV 2002     Bug fix release
+
+       Fixed bugs with initrds and device nodes
+
+1.0    23 OCT 2002     Initial release
diff --git a/SQUASHFS/squashfs-tools-4.4/COPYING b/SQUASHFS/squashfs-tools-4.4/COPYING
new file mode 100644 (file)
index 0000000..d159169
--- /dev/null
@@ -0,0 +1,339 @@
+                    GNU GENERAL PUBLIC LICENSE
+                       Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
+ 51 Franklin Street, 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 Lesser 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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    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 Street, 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.
+
+  <signature of Ty Coon>, 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 Lesser General
+Public License instead of this License.
diff --git a/SQUASHFS/squashfs-tools-4.4/INSTALL b/SQUASHFS/squashfs-tools-4.4/INSTALL
new file mode 100644 (file)
index 0000000..5eed146
--- /dev/null
@@ -0,0 +1,44 @@
+                       INSTALLING SQUASHFS
+
+1. Kernel support
+-----------------
+
+This release is for 2.6.29 and newer kernels.  Kernel patching is not necessary.
+
+Extended attribute support requires 2.6.35 or newer kernels.  But
+file systems with extended attributes can be mounted on 2.6.29 and
+newer kernels (the extended attributes will be ignored with a warning).
+
+LZO compression support requires 2.6.36 or newer kernels.
+
+XZ compression support requires 2.6.38 or newer kernels.
+
+LZ4 compression support requires 3.11 or newer kernels.
+
+ZSTD compression support requires 4.14 or newer kernels.
+
+2. Building squashfs tools
+--------------------------
+
+The squashfs-tools directory contains the source code for the Mksquashfs and
+Unsquashfs programs.  These can be compiled by typing make (or sudo make
+install to install in /usr/local/bin) within that directory.
+
+2.1 Compressors supported
+
+By default the Makefile is configured to build Mksquashfs and Unsquashfs
+with GZIP suppport.  Read the Makefile in squashfs-tools for instructions on
+building LZO, LZ4, XZ and ZSTD compression support.
+
+2.2 Extended attribute support
+
+By default the Makefile is configured to build Mksquashfs and Unsquashfs
+with extended attribute support.  Read the Makefile in squashfs-tools for
+instructions on how to disable extended attribute support, if not supported
+by your distribution/C library, or if it is not needed.
+
+2.3 Reproducible builds
+
+By default the Makefile is configured to build Mksquashfs with reproducible
+output as default.  Read the Makefile in squashfs-tools for instructions
+on how to disable reproducible output as default if desired.
diff --git a/SQUASHFS/squashfs-tools-4.4/README b/SQUASHFS/squashfs-tools-4.4/README
new file mode 100644 (file)
index 0000000..478c2fa
--- /dev/null
@@ -0,0 +1,15 @@
+GitHub
+
+This is now the main development repository for Squashfs-Tools.
+
+There are older repositories on Sourceforge and kernel.org.  These
+are currently out of date, but should be updated in the near future.
+
+The kernel directories are obsolete, all kernel code is now in
+mainline at www.kernel.org.
+
+kernel-2.4: If you still need Squashfs support in the 2.4 kernel
+then use the squashfs 3.2-r2 release.  This is the last release
+that has a Mksquashfs which generates filesystems mountable with
+Squashfs patched 2.4 kernels.  This release has patches for 2.4
+kernels (but they have not been updated since the 3.1 release).
diff --git a/SQUASHFS/squashfs-tools-4.4/README-4.4 b/SQUASHFS/squashfs-tools-4.4/README-4.4
new file mode 100644 (file)
index 0000000..404bf86
--- /dev/null
@@ -0,0 +1,316 @@
+       SQUASHFS 4.4 - A squashed read-only filesystem for Linux
+
+       Copyright 2002-2019 Phillip Lougher <phillip@squashfs.org.uk>
+
+       Released under the GPL licence (version 2 or later).
+
+Welcome to Squashfs 4.4.  This is the first release in over 5 years, and
+there are substantial improvements: reproducible builds, new compressors,
+CVE fixes, security hardening and new options for Mksquashfs/Unsquashfs.
+
+Please see the INSTALL file for instructions on installing the tools,
+and the USAGE file for documentation on how to use the tools.
+
+Summary of changes (and sections below)
+---------------------------------------
+
+1. Mksquashfs now generates reproducible images by default.  Mkfs time and
+   file timestamps can also be specified.
+
+2. Support for the Zstandard (ZSTD) compression algorithm has been added.
+
+3. Pseudo files now support symbolic links.
+
+4. CVE-2015-4645 and CVE-2015-4646 have been fixed.
+
+5. Unsquashfs has been further hardened against corrupted filestems.
+
+6. Unsquashfs is now more strict about error handling.
+
+7. Miscellaneous new options and major bug fixes for Mksquashfs.
+
+8. Miscellaneous new options and major bug fixes for Unsquashfs.
+
+9. Squashfs-tools 4.4 is compatible with all earlier 4.x filesystems
+   and releases.
+
+
+1. Introducing reproducible builds
+----------------------------------
+
+Ever since Mksquashfs was parallelised back in 2006, there
+has been a certain randomness in how fragments and multi-block
+files are ordered in the output filesystem even if the input
+remains the same.
+
+This is because the multiple parallel threads can be scheduled
+differently between Mksquashfs runs.  For example, the thread
+given fragment 10 to compress may finish before the thread
+given fragment 9 to compress on one run (writing fragment 10
+to the output filesystem before fragment 9), but, on the next
+run it could be vice-versa.  There are many different scheduling
+scenarios here, all of which can have a knock on effect causing
+different scheduling and ordering later in the filesystem too.
+
+Mkquashfs doesn't care about the ordering of fragments and
+multi-block files within the filesystem, as this does not
+affect the correctness of the filesystem.
+
+In fact not caring about the ordering, as it doesn't matter, allows
+Mksquashfs to run as fast as possible, maximising CPU and I/O
+performance.
+
+But, in the last couple of years, Squashfs has become used in
+scenarios (cloud etc) where this randomness is causing problems.
+Specifically this appears to be where downloaders, installers etc.
+try to work out the differences between Squashfs filesystem
+updates to minimise the amount of data that needs to transferred
+to update an image.
+
+Additionally, in the last couple of years has arisen the notion
+of reproducible builds, that is the same source and build
+environment etc should be able to (re-)generate identical
+output.  This is usually for verification and security, allowing
+binaries/distributions to be checked for malicious activity.
+See https://reproducible-builds.org/ for more information.
+
+Mksquashfs now generates reproducible images by default.
+Images generated by Mksquashfs will be ordered identically to
+previous runs if the same input has been supplied, and the
+same options used.  
+
+1.1.1 Dealing with timestamps
+
+Timestamps embedded in the filesystem will stiil cause differences.
+Each new run of Mksquashfs will produce a different mkfs (make filesystem)
+timestamp in the super-block.  Moreover if any file timestamps have changed
+(even if the content hasn't), this will produce a difference.
+
+To prevent timestamps from producing differences, the following
+new Mksquashfs options have been added.
+
+1.1.2 -mkfs-time <time>
+
+This option takes a positive time value (which is the number
+of seconds since the epoch of 1970-01-01 00:00:00 UTC), and sets
+the file system timestamp to that.
+
+Squashfs uses an unsigned 32-bit integer to store time, and the
+time given should be in that range.
+
+Obviously you can use the date command to convert dates into
+this value, i.e.
+
+% mksquashfs source source.sqsh -mkfs-time $(date +%s -d "Jan 1 2019 19:00")
+
+1.1.3 -all-time <time>
+
+This option takes a positive time value (which is the number
+of seconds since the epoch of 1970-01-01 00:00:00 UTC), and sets
+the timestamp on all files to that (but not the mkfs time).
+
+1.1.4 environment variable SOURCE_DATE_EPOCH
+
+As an alternative to the above command line options, you can
+set the environment variable SOURCE_DATE_EPOCH to a time value.
+
+This value will be used to set the mkfs time.  Also any
+file timestamps which are after SOURCE_DATE_EPOCH will be
+clamped to SOURCE_DATE_EPOCH.
+
+See https://reproducible-builds.org/docs/source-date-epoch/
+for more information.
+
+Note: both SOURCE_DATE_EPOCH and the command line options cannot
+be used at the same time.  They are different ways to do the same thing,
+and both have FORCE sematics which mean they can't be over-ridden
+elsewhere (otherwise it would defeat the purpose).
+
+1.1.5 -not-reproducible
+
+This option tells Mksquashfs that the files do not have to be
+strictly ordered.  This will make Mksquashfs behave like version 4.3.
+
+
+2. Zstandard (ZSTD) compression added
+-------------------------------------
+
+This is named zstd.  It supports the following
+compression options.
+
+       zstd
+         -Xcompression-level <compression-level>
+               <compression-level> should be 1 .. 22 (default 15)
+
+
+3. Pseudo file symbolic link support added
+------------------------------------------
+
+Pseudo definition
+
+Filename s mode uid gid symlink
+
+uid and gid can be either specified as a decimal number, or by name.
+
+Note mode is ignored, as symlinks always have "rwxrwxrwx" permissions.
+
+For example:
+
+symlink s 0 root root example
+
+creates a symlink "symlink" to file "example" with root uid/gid.
+
+
+4. CVE-2015-2015-4645 and CVE-2015-4646
+---------------------------------------
+
+These CVEs were raised due to Unsquashfs having variable overflow and
+stack overflow in a number of vulnerable functions.
+
+All instances of variable overflow and stack overflow have been
+removed.
+
+
+5. Unsquashfs hardened against corrupted filestems
+--------------------------------------------------
+
+The filesystem super-block values and filesystem metadata tables
+are further sanity checked.  More importantly, these values are now
+checked for validity against other values in the metadata tables, and
+these values must match.
+
+
+6. Unsquashfs is now more strict about error handling
+-----------------------------------------------------
+
+Unsquashfs splits errors into two categories: fatal errors and non-fatal
+errors.  In this release a significant number of errors that were previously
+non-fatal have been hardened to fatal.
+
+Fatal errors are those which cause Unsquashfs to abort instantly.
+These are generally due to failure to read the filesystem (corruption),
+and/or failure to write files to the output filesystem, due to I/O error
+or out of space.  Generally anything which is unexpected is a fatal error.
+
+Non-fatal errors are generally where support is lacking in the
+output filesystem, and it can be considered to be an expected failure.
+This includes the inability to write extended attributes (xattrs) to
+a filesystem that doesn't support them, the inability to create files on
+filesystem that doesn't support them (i.e. symbolic links on VFAT), and the
+inability to execute privileged operations as a user-process.
+
+The user may well know the filesystem cannot support certain operations
+and would prefer Unsquashfs to ignore then without aborting.
+
+Two new options have been added:
+
+6.1. -ignore-errors
+
+This makes Unsquashfs behave like previous versions, and treats more
+errors as non-fatal.
+
+6.2. -strict-errors
+
+This makes Unsquashfs treat every error as fatal, and it will abort
+instantly.
+
+
+7. Miscellaneous new options and major bug fixes for Mksquashfs
+---------------------------------------------------------------
+
+7.1. -root-mode <mode>
+
+This sets the permissions of the root directory to the octal <mode>.
+This is mostly intended for when multiple sources are specified on
+the command line.  In this instance Mksquashfs produces a dummy top level
+directory with permissions 0777 (rwxrwxrwx).  This option allows the
+permissions to be changed.  But the option can also be used when a single
+source is specified.
+
+7.2. -quiet
+
+This suppresses all output from Mksquashfs, except the progress bar.
+
+The progress bar can disabled with -no-progress to produce completely
+silent output.
+
+This new option is useful for scripts.
+
+7.3. -noId
+
+This is similar to the pre-existing -noI option, except, it specifies that
+only the Id table (uids and gids) should be uncompressed.  This option was
+added to enable a use-case where uids and gids need to be updated after
+filesystem generation.
+
+7.4. -offset <offset>
+
+This option skips <offset> bytes at the beginning of the output filesystem.
+
+Optionally a suffix of K, M or G can be given to specify Kbytes, Mbytes or
+Gbytes respectively.
+
+7.5. Update lz4 wrapper to use new functions introduced in 1.7.0
+
+7.5. Bug fix, don't allow "/" pseudo filenames
+
+7.6. Bug fix, allow quoting of pseudo files, to better handle filenames with
+     spaces
+
+7.7. Fix compilation with glibc 2.25+
+
+
+8. Miscellaneous new options and major bug fixes for Unsquashfs
+---------------------------------------------------------------
+
+8.1. -lln[umeric]
+
+This is similar to the "-lls" option but displays uids and gids numerically.
+
+8.2. -lc option
+
+This is similar to the "-ls" option except it only displays files and empty
+directories.
+
+8.3. -llc option
+
+As "-llc" option but displays file attributes.
+
+8.4. -offset <offset>
+
+This option skips <offset> bytes at the beginning of the input filesystem.
+
+Optionally a suffix of K, M or G can be given to specify Kbytes, Mbytes or
+Gbytes respectively.
+
+8.5. -quiet
+
+This suppresses all output from Unsquashfs, except the progress bar.
+
+The progress bar can disabled with -no-progress to produce completely
+silent output.
+
+This new option is useful for scripts.
+
+8.6. -UTC
+
+This option makes Unsquashfs display all times in the UTC time zone rather
+than using the default local time zone.
+
+8.7. Update lz4 wrapper to use new functions introduced in 1.7.0
+
+8.8. Bug fix, fatal and non-fatal errors now set the exit code to 1
+
+8.9. Bug fix, fix time setting for symlinks
+
+8.10. Bug fix, try to set sticky-bit when running as a user process
+
+8.11. Fix compilation with glibc 2.25+
+
+
+9. Compatiblity
+---------------
+
+Mksquashfs 4.4 generates 4.0 filesystems.  These filesystems are fully
+compatible/interchangable with filesystems generated by Mksquashfs 4.x and are
+mountable on 2.6.29 and later kernels.
diff --git a/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/DONATIONS b/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/DONATIONS
new file mode 100644 (file)
index 0000000..b4653bb
--- /dev/null
@@ -0,0 +1,15 @@
+Help sponsor Squashfs development!
+
+Maintaining and improving Squashfs is a lot of work, but Squashfs is one of
+the only widely used Linux file systems that has no company backing.  Squashfs
+development is funded soley by the author, partially supported by donations
+from companies and individuals that want to improve Squashfs for themselves
+and others.
+
+There's lots of exciting new improvements to Squashfs in the pipeline, and
+if your company is a serious user of Squashfs, please consider accelerating
+development of Squashfs by donating.
+
+Donatations can be made from the Squashfs sourceforge homepage, or if you
+prefer by contacting the author privately.
+
diff --git a/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/PERFORMANCE.README b/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/PERFORMANCE.README
new file mode 100644 (file)
index 0000000..20c2827
--- /dev/null
@@ -0,0 +1,177 @@
+* Obsolete *
+
+This file is now largely obsolete, considering
+it refers to Squashfs 2.1 (which was released in 2006)
+and older releases.
+
+GENERAL INFORMATION ON PERFORMANCE TESTS
+----------------------------------------
+
+The following performance tests were based on two file sets: the
+liveCD filesystem from the Ubuntu liveCD (Warty release), and the
+liveCD filesystem from the Damn Small Linux liveCD (release 0.8.4).
+The Ubuntu liveCD filesystem was used to test filesystem performance
+from CDROM and hard disk for Zisofs, Cloop, Squashfs 2.0 and Squashfs2.1.
+CRAMFS filesystem performance could not be tested for this filesystem
+bacause it exceeds the maximum supported size of CRAMFS.  To test
+CRAMFS performance against Squashfs, the liveCD filesystem from
+Damn Small Linux was used.
+
+NOTE: the usual warnings apply to these results, they are provided for
+illustrative purposes only, and due to different hardware and/or file data, you
+may obtain different results.  As such the results are provided "as is" without
+any warranty (either express or implied) and you assume all risks as to their
+quality and accuracy.
+
+1. Ubuntu liveCD performance tests
+
+   ext3 uncompressed size      1.4 GB
+   Zisofs compressed size      589.81 MB
+   Cloop compressed size       471.89 MB
+   Squashfs2.0 compressed size 448.58 MB
+   Squashfs2.1 compressed size 448.58 MB
+
+1.1 Performance tests from CDROM
+
+1.1.1 Directory Lookup performance
+
+  Time taken to perform "ls -lR --color=alawys | cat > /dev/null" on filesystem
+  mounted from CDROM
+
+  Zisofs       49.88 seconds (User 2.60 secs, Sys 11.19 secs)
+  Cloop        20.80 seconds (User 2.71 secs, Sys 13.50 secs)
+  Squashfs2.0  16.56 seconds (User 2.42 secs, Sys 10.37 secs)
+  Squashfs2.1  10.14 seconds (User 2.48 secs, Sys 4.44 secs)
+
+1.1.2 Sequential I/O performance
+
+  Time taken to perform "tar cf - | cat > /dev/null" on filesystem mounted
+  from CDROM
+
+  Zisofs       27 minutes 28.54 seconds (User 3.00 secs, Sys 1 min 4.80 secs)
+  Cloop        5 minutes 55.72 seconds (User 2.90 secs, Sys 3 min 37.90 secs)
+  Squashfs2.0  5 minutes 20.87 seconds (User 2.33 secs, Sys 56.98 secs)
+  Squashfs2.1  5 minutes 15.46 seconds (user 2.28 secs, Sys 51.12 secs)
+
+1.1.3 Random I/O performance
+
+  Random access pattern generated by "find /mnt -type f -printf "%s %p\n" | sort
+  -g | awk '{ printf $2 }' > /tmp/sort
+
+  Time taken to perform "cpio -o --quiet -H newc < /tmp/sort > /dev/null"
+  on filesystem mounted from CDROM
+
+  Zisofs       101 minutes 29.65 seconds (User 5.33 secs, Sys  1 min 17.20 secs)
+  Cloop        35 minutes 27.51 seconds (user 5.93 secs, Sys 4 mins 30.23 secs)
+  Squashfs2.0  21 minutes 53.05 seconds (user 5.71 secs, Sys 2 mins 36.59 secs)
+  Squashfs2.1  21 minutes 46.99 seconds (User 5.80 secs, Sys 2 mins 31.88 secs)
+
+
+1.2 Performance tests from Hard disk
+
+1.2.1 Directory Lookup performance
+
+  Time taken to perform "ls -lR --color=alawys | cat > /dev/null" on filesystem
+  mounted from Hard disk 
+
+  Zisofs       17.29 seconds (User 2.62 secs, Sys 11.08 secs)
+  Cloop        16.46 seconds (User 2.63 secs, Sys 13.41 secs)
+  Squashfs2.0  13.75 seconds (User 2.44 secs, Sys 11.00 secs)
+  Squashfs2.1  6.94 seconds (User 2.44 secs, Sys 4.48 secs)
+
+1.2.2 Sequential I/O performance
+
+  Time taken to perform "tar cf - | cat > /dev/null" on filesystem mounted
+  from Hard disk
+
+  Zisofs       1 minute 21.47 seconds (User 2.73 secs, Sys 54.44 secs)
+  Cloop        1 minute 34.06 seconds (user 2.85 secs, Sys 1 min 12.13 secs)
+  Squashfs2.0  1 minute 21.22 seconds (User 2.42 secs, Sys 56.21 secs)
+  Squashfs2.1  1 minute 15.46 seconds (User 2.36 secs, Sys 49.78 secs)
+
+1.2.3 Random I/O performance
+
+  Random access pattern generated by "find /mnt -type f -printf "%s %p\n" | sort
+  -g | awk '{ printf $2 }' > /tmp/sort
+
+  Time taken to perform "cpio -o --quiet -H newc < /tmp/sort > /dev/null"
+  on filesystem mounted from Hard disk
+
+  Zisofs       11 minutes 13.64 seconds (User 5.08 secs, Sys 52.62 secs)
+  Cloop        5 minutes 37.93 seconds (user 6 secs, Sys 2 mins 22.38 secs)
+  Squashfs2.0  5 minutes 7.11 seconds (user 5.63 secs, Sys 2 mins 35.23 secs)
+  Squashfs2.1  5 minutes 1.87 seconds (User 5.71 secs, Sys 2 mins 29.98 secs)
+
+
+2. Damn Small Linux liveCD performance tests
+
+   ext3 uncompressed size      126 MB
+   CRAMFS compressed size      52.19 MB
+   Squashfs2.0 compressed size 46.52 MB
+   Squashfs2.1 compressed size 46.52 MB
+
+2.1 Performance tests from CDROM
+
+2.1.1 Directory Lookup performance
+
+  Time taken to perform "ls -lR --color=alawys | cat > /dev/null" on filesystem
+  mounted from CDROM
+
+  CRAMFS       10.85 seconds (User 0.39 secs, Sys 0.98 secs)
+  Squashfs2.0  2.97 seconds (User 0.36 secs, Sys 2.15 secs)
+  Squashfs2.1  2.43 seconds (User 0.40 secs, Sys 1.42 secs)
+
+2.1.2 Sequential I/O performance
+
+  Time taken to perform "tar cf - | cat > /dev/null" on filesystem mounted
+  from CDROM
+
+  CRAMFS       55.38 seconds (User 0.34 secs, Sys 6.98 secs)
+  Squashfs2.0  35.99 seconds (User 0.30 secs, Sys 6.35 secs)
+  Squashfs2.1  33.83 seconds (User 0.26 secs, Sys 5.56 secs)
+
+2.1.3 Random I/O performance
+
+  Random access pattern generated by "find /mnt -type f -printf "%s %p\n" | sort
+  -g | awk '{ printf $2 }' > /tmp/sort
+
+  Time taken to perform "cpio -o --quiet -H newc < /tmp/sort > /dev/null"
+  on filesystem mounted from CDROM
+
+
+  CRAMFS        3 minutes 1.68 seconds (User 0.54 secs, Sys 9.51 secs)
+  Squashfs2.0   1 minute 39.45 seconds (User 0.57 secs, Sys 13.14 secs)
+  Squashfs2.1   1 minute 38.41 seconds (User 0.58 secs, Sys 13.08 secs)
+
+2.2 Performance tests from Hard disk
+
+2.2.1 Directory Lookup performance
+
+  Time taken to perform "ls -lR --color=alawys | cat > /dev/null" on filesystem
+  mounted from Hard disk
+
+  CRAMFS       1.77 seconds (User 0.53 secs, Sys 1.21 secs)
+  Squashfs2.0  2.67 seconds (User 0.41 secs, Sys 2.25 secs)
+  Squashfs2.1  1.87 seconds (User 0.41 secs, Sys 1.46 secs)
+
+2.2.2 Sequential I/O performance
+
+  Time taken to perform "tar cf - | cat > /dev/null" on filesystem mounted
+  from Hard disk 
+
+  CRAMFS       6.80 seconds (User 0.36 secs, Sys 6.02 secs)
+  Squashfs2.0  7.23 seconds (User 0.29 secs, Sys 6.62 secs)
+  Squashfs2.1  6.53 seconds (User 0.31 secs, Sys 5.82 secs)
+
+2.2.3 Random I/O performance
+
+  Random access pattern generated by "find /mnt -type f -printf "%s %p\n" | sort
+  -g | awk '{ printf $2 }' > /tmp/sort
+
+  Time taken to perform "cpio -o --quiet -H newc < /tmp/sort > /dev/null"
+  on filesystem mounted from Hard disk 
+
+
+  CRAMFS       28.55 seconds (User 0.49 secs, Sys 6.49 secs)
+  Squashfs2.0  25.44 seconds (User 0.58 secs, Sys 13.17 secs)
+  Squashfs2.1  24.72 seconds (User 0.56 secs, Sys 13.15 secs)
diff --git a/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-2.0 b/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-2.0
new file mode 100644 (file)
index 0000000..41931d8
--- /dev/null
@@ -0,0 +1,161 @@
+NOTE:  This the original README for version 2.0.  It is retained as it
+contains information about the fragment design.  A description of the new 2.0
+mksquashfs options has been added to the main README file, and that
+file should now be consulted for these.
+
+       SQUASHFS 2.0 - A squashed read-only filesystem for Linux
+
+       Copyright 2004 Phillip Lougher (plougher@users.sourceforge.net)
+
+       Released under the GPL licence (version 2 or later).
+
+Welcome to the final release of Squashfs version 2.0!  A lot of changes to the
+filesystem have been made under the bonnet (hood).  Squashfs 2.0 uses fragment
+blocks and larger blocks (64K) to improve compression ratio by about 5 - 20%
+over Squashfs 1.0 depending on the files being compressed.  Using fragment
+blocks allows Squashfs 2.0 to achieve better compression than cloop and similar
+compression to tgz files while retaining the I/O efficiency of a compressed
+filesystem.
+
+Detailed changes:
+
+1. Squashfs 2.0 has added the concept of fragment blocks (see later discussion).
+   Files smaller than the file block size (64K in Squashfs 2.0) and optionally
+   the remainder of files that do not fit fully into a block (i.e. the last 32K
+   in a 96K file) are packed into shared fragments and compressed together.
+   This achieves on average 5 - 20% better compression than Squashfs 1.x.
+
+2. The maximum block size has been increased to 64K.
+
+3. The maximum number of UIDs has been increased to 256 (from 48 in 1.x).
+
+4. The maximum number of GIDs has been increased to 256 (from 15 in 1.x).
+
+5. New mksquashfs -all-root, -root-owned, -force-uid, and -force-gid
+   options.  These allow the uids/gids of files in the generated
+   filesystem to be specified, overriding the uids/gids in the
+   source filesystem.
+
+6. Initrds are now supported for kernels 2.6.x.
+
+7. Removal of sleep_on() function call in 2.6.x patch, to allow Squashfs
+   to work on the Fedora rc2 kernel.
+
+8. AMD64, check-data and gid bug fixes.
+
+9. Numerous small bug fixes have been made.
+
+10. New patch for Linux 2.6.7.
+
+
+New Squashfs 2.0 options
+------------------------
+
+-noF or -noFragmentCompression
+
+       Do not compress the fragments.  Added for compatibility with noI and
+       noD, probably not that useful.
+
+-no-fragments
+
+       Do not use fragment blocks, and rather generate a filesystem
+       similar to a Squashfs 1.x filesystem.  It will of course still
+       be a Squashfs 2.0 filesystem but without fragments, and so
+       it won't be mountable on a Squashfs 1.x system.
+
+-always-use-fragments
+
+       By default only small files less than the block size are packed into
+       fragment blocks.  The ends of files which do not fit fully into a block,
+       are NOT by default packed into fragments.  To illustrate this, a
+       100K file has an initial 64K block and a 36K remainder.  This
+       36K remainder is not packed into a fragment by default.  This is
+       because to do so leads to a 10 - 20% drop in sequential I/O
+       performance, as a disk head seek is needed to seek to the initial
+       file data and another disk seek is need to seek to the fragment
+       block.
+
+       Specify this option if you want file remainders to be packed into
+       fragment blocks.  Doing so may increase the compression obtained
+       BUT at the expense of I/O speed.
+
+-no-duplicates
+
+       Do not detect duplicate files.
+
+-all-root
+-root-owned
+
+       These options (both do exactly the same thing), force all file
+       uids/gids in the generated Squashfs filesystem to be root.
+       This allows root owned filesystems to be built without root access
+       on the host machine.
+
+-force-uid uid
+
+       This option forces all files in the generated Squashfs filesystem to
+       be owned by the specified uid.  The uid can be specified either by
+       name (i.e. "root") or by number.
+
+-force-gid gid
+
+       This option forces all files in the generated Squashfs filesystem to
+       be group owned by the specified gid.  The gid can be specified either by
+       name (i.e. "root") or by number.
+
+
+Compression improvements example
+--------------------------------
+
+The following is the compression results obtained compressing the 2.6.6
+linux kernel source using CRAMFS, Cloop (with iso filesystem), Squashfs 1.3 and
+Squashfs 2.0 (results generated using big-endian filesystems).
+
+In decreasing order of size:
+
+       CRAMFS          62791680 bytes (59.9M)
+       Squashfs 1.x    51351552 bytes (48.9M)
+       Cloop           46118681 bytes (44.0M)
+       Squashfs 2.0    45604854 bytes (43.5M)
+
+
+The Squashfs 1.x filesystem is 12.6% larger than the new 2.0 filesystem.
+The cloop filesystem is 1.1% larger than the Squashfs 2.0 filesystem.
+
+
+Fragment blocks in Squashfs 2.0
+-------------------------------
+
+Squashfs like all other compressed filesystems compresses files individually
+on a block by block basis.  This is performed to allow mounting and
+de-compression of files on a block by block basis without requiring the entire
+filesystem to be decompressed.  This is in contrast to data-based compression
+schemes which compress without understanding the underlying filesystem (i.e.
+cloop and tgz files) and which, therefore, do not compress files individually.
+Each approach has advantages and disadvantages, data-based systems have better
+compression because compression is always performed at the maximum block size
+(64K in cloop) irrespective of the size of each file (which could be less than
+the block size).  Compressed filesystems tend to be faster at I/O because
+they understand the filesystem and therefore employ better caching stategies
+and read less un-needed data from the filesystem.
+
+Fragment blocks in Squashfs 2.0 solves this problem by packing files (and
+optionally the ends of files) which are smaller than the block size into
+shared blocks, which are compressed together.  For example five files each of
+10K will be packed into one shared fragment of 50K and compressed together,
+rather than being compressed in five 10K blocks.
+
+This scheme produces a hybrid filesystem, retaining the I/O efficiency
+of a compressed filesystem, while obtaining the compression efficiency
+of data-based schemes by compressing small files together.
+
+
+Squashfs 1.x and Squashfs 2.0 compatibility
+-------------------------------------------
+
+Appending to Squashfs 1.x filesystems is not supported.  If you wish to append
+to 1.x filesystems, then either use the original mksquashfs, or convert them
+to Squashfs 2.0 by mounting the filesystem and running the 2.0 mksquashfs
+on the mounted filesystem.
+
+Mounting Squashfs 1.x filesystems IS supported by the 2.0 kernel patch.
diff --git a/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-2.0-AMD64 b/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-2.0-AMD64
new file mode 100644 (file)
index 0000000..5569617
--- /dev/null
@@ -0,0 +1,18 @@
+Information for amd64 users
+---------------------------
+
+All previous releases of Squashfs (2.0-alpha and older) generate incorrect
+filesystems on amd64 machines.  These filesystems work correctly on amd64
+machines, but cannot be mounted on non-amd64 machines.  Likewise, filesystems
+generated on non amd64 machines could not be mounted on amd64 machines.
+This bug was caused by the different size of the "time_t" definition used in
+SquashFS filesystem structures.
+
+This bug is now fixed in this release.  However, all amd64 filesystems
+generated by previous releases will not be mountable on amd64 machines
+with this release.  If you have pre-existing amd64 generated filesystems,
+it is important that you recreate the filesystem.  This can be performed
+by mounting the filesystem using a kernel with the original patch
+(i.e. a 2.0-alpha or older patch) and running the SquashFS 2.0
+(i.e. this release) mksquashfs tool to create a new SquashFS filesystem.
+
diff --git a/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-2.1 b/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-2.1
new file mode 100644 (file)
index 0000000..e70167e
--- /dev/null
@@ -0,0 +1,87 @@
+       SQUASHFS 2.1 - A squashed read-only filesystem for Linux
+
+       Copyright 2004 Phillip Lougher (plougher@users.sourceforge.net)
+
+       Released under the GPL licence (version 2 or later).
+
+Welcome to Squashfs version 2.1-r2.  Squashfs 2.1 introduces indexed
+directories which considerably speed up directory lookup (ls, find etc.) for
+directories which are greater than 8K in size.  All directories are now also
+sorted alphabetically which further speeds up directory lookup.  Many smaller
+improvements have also been made to this release, please see the CHANGES file
+entry for detailed changes.
+
+1. DIRECTORY SPEED IMPROVEMENT EXAMPLES
+---------------------------------------
+
+To give an indication of the directory speed improvements a number of test
+results are shown here.  There is in addition a new PERFORMANCE.README file
+which gives details of I/O and lookup performance for Squashfs 2.1 against
+the Zisofs, Cloop and CRAMFS filesystems.
+
+example 1:
+
+Filesystems generated from a single directory of 72,784 files (2.6 MB
+directory size).  Each file is 10 bytes in size (the test is directory
+lookup and so the file size isn't an issue).  The ext3 uncompressed
+directory size is 288 MB (presumably because of one file per block).
+
+Zisofs compressed size        153.50 MB
+Cloop (isofs) compressed size 1.74 MB
+Squashfs2.1 compressed size   612 KB (0.60 MB)
+
+Time taken to perform "ls -lR --color=always | cat > /dev/null" on
+filesystems mounted on hard disk.
+
+Zisofs       35 minutes 7.895 seconds (User 7.868 secs, Sys 34 mins 5.621 secs)
+Cloop        35 minutes 12.765 seconds (User 7.771 secs, Sys 34 mins 3.869 secs)
+Squashfs2.1  19 seconds (User 5.119 secs, Sys 14.547 secs)
+
+example 2:
+
+Filesystems were generated from the Ubuntu Warty livecd (original uncompressed
+size on ext3 is 1.4 GB).
+
+Zisofs compressed size        589.81 MB
+Cloop (isofs) compressed size 471.19 MB
+Squashfs2.0 compressed size   448.58 MB
+Squashfs2.1 compressed size   448.58 MB
+
+Time taken to perform "ls -lR --color=always | cat > /dev/null" on
+filesystems mounted on hard disk.
+
+Zisofs        49.875 seconds (User time 2.589 secs, Sys 11.194 secs)
+Cloop         20.797 seconds (User time 2.706 secs, Sys 13.496 secs)
+Squashfs2.0   16.556 seconds (User time 2.424 secs, Sys 10.371 secs)
+Squashfs2.1   10.143 seconds (User time 2.475 secs, Sys 4.440 secs)
+
+
+NOTE: the usual warnings apply to these results, they are provided for
+illustrative purposes only, and due to different hardware and/or file data, you
+may obtain different results.  As such the results are provided "as is" without
+any warranty (either express or implied) and you assume all risks as to their
+quality and accuracy.
+
+2. NEW MKSQUASHFS OPTIONS
+-------------------------
+
+There is only one extra option "-2.0".  This tells mksquashfs to generate
+a filesystem which is mountable with Squashfs version 2.0.
+
+3. APPENDING AND MOUNTING SQUASHFS 2.0 FILESYSTEMS
+--------------------------------------------------
+
+Mounting 2.0 filesystems is supported by Squashfs 2.1.  In addition
+mksquashfs v2.1 can append to 2.0 filesystems, although the generated
+filesystem will still be a 2.0 filesystem.
+
+4. DONATIONS
+------------
+
+If you find Squashfs useful then please consider making a donation,
+particularly if you use Squashfs in a commercial product.  Please consider
+giving something back especially if you're making money from it.
+
+Off the Squashfs subject somewhat I'm currently looking for another
+job doing Linux kernel or filesystems work.  If you know of any such
+work that can be performed from the UK then please get in touch.  Thanks.
diff --git a/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-3.0 b/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-3.0
new file mode 100644 (file)
index 0000000..fd16dd3
--- /dev/null
@@ -0,0 +1,60 @@
+       SQUASHFS 3.0 - A squashed read-only filesystem for Linux
+
+       Copyright 2002-2006 Phillip Lougher <phillip@lougher.org.uk>
+
+       Released under the GPL licence (version 2 or later).
+
+Welcome to the first release of Squashfs version 3.0.  Squashfs 3.0 has the
+the following improvements to 2.x.
+
+       1. Filesystems are no longer limited to 4 GB.  In
+          theory 2^64 or 4 exabytes is now supported.
+
+       2. Files are no longer limited to 4 GB.  In theory the maximum
+          file size is 4 exabytes.
+
+       3. Metadata (inode table and directory tables) are no longer
+          restricted to 16 Mbytes.
+
+       4. Hardlinks are now suppported.
+
+       5. Nlink counts are now supported.
+
+       6. Readdir now returns '.' and '..' entries.
+
+       7. Special support for files larger than 256 MB has been added to
+          the Squashfs kernel code for faster read access.
+
+       8. Inode numbers are now stored within the inode rather than being
+          computed from inode location on disk (this is not so much an
+          improvement, but a change forced by the previously listed
+          improvements).
+
+There is a new Unsquashfs utility (in squashfs-tools) than can be used to
+decompress a filesystem without mounting it.
+
+Squashfs 3.0 supports 2.x filesystems.  Support for 1.x filesystems
+will be added in the future.
+
+1. UNSQUASHFS
+-------------
+
+Unsquashfs has the following options:
+
+SYNTAX: unsquashfs [-ls | -dest] filesystem
+       -version                print version, licence and copyright information
+       -info                   print files as they are unsquashed
+       -ls                     list filesystem only
+       -dest <pathname>        unsquash to <pathname>, default "squashfs-root"
+
+The "-ls" option can be used to list the contents of a filesystem without
+decompressing the filesystem data itself.
+
+The "-info" option forces Unsquashfs to print each file as it is decompressed.
+
+The "-dest" option specifies the directory that is used to decompress
+the filesystem data.  If this option is not given then the filesystem is
+decompressed to the directory "squashfs-root" in the current working directory.
+
+Unsquashfs can decompress 3.0 filesystems.  Support for 2.x and 1.x
+filesystems will be added in the future.
diff --git a/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-3.1 b/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-3.1
new file mode 100644 (file)
index 0000000..0e1ee79
--- /dev/null
@@ -0,0 +1,158 @@
+       SQUASHFS 3.1 - A squashed read-only filesystem for Linux
+
+       Copyright 2002-2006 Phillip Lougher <phillip@lougher.org.uk>
+
+       Released under the GPL licence (version 2 or later).
+
+Welcome to Squashfs version 3.1-r2.  Squashfs 3.1 has major improvements to
+the Squashfs tools (Mksquashfs and Unsquashfs), some major bug fixes, new
+kernel patches, and various other smaller improvements and bug fixes.
+Please see the CHANGES file for a detailed list.
+
+1. MKSQUASHFS
+-------------
+
+Mksquashfs has been rewritten and it is now multi-threaded.  It offers
+the following improvements:
+
+1. Parallel compression.  By default as many compression and fragment
+compression threads are created as there are available processors.
+This significantly speeds up performance on SMP systems.
+
+2. File input and filesystem output is peformed in parallel on separate
+threads to maximise I/O performance.  Even on single processor systems
+this speeds up performance by at least 10%.
+
+3. Appending has been significantly improved, and files within the
+filesystem being appended to are no longer scanned and checksummed.  This
+significantly improves append time for large filesystems.
+
+4. File duplicate checking has been optimised, and split into two separate
+phases.  Only files which are considered possible duplicates after the
+first phase are checksummed and cached in memory.
+
+5. The use of swap memory was found to significantly impact performance. The
+amount of memory used to cache the file is now a command line option, by default
+this is 512 Mbytes.
+
+1.1 NEW COMMAND LINE OPTIONS
+----------------------------
+
+The new Mksquashfs program has a couple of extra command line options
+which can be used to control the new features:
+
+-processors <processors>
+
+This specifies the number of processors used by Mksquashfs.
+By default this is the number of available processors.
+
+-read_queue <size in Mbytes>
+
+This specifies the size of the file input queue used by the reader thread.
+This defaults to 64 Mbytes.
+
+-write_queue <size in Mbytes>
+
+This specifies the size of the filesystem output queue used by the
+writer thread.  It also specifies the maximum cache used in file
+duplicate detection (the output queue is shared between these tasks).
+This defaults to 512 Mbytes.
+
+1.2 PERFORMANCE RESULTS
+-----------------------
+
+The following results give an indication of the speed improvements.  Two
+example filesystems were tested, a liveCD filesystem (about 1.8 Gbytes
+uncompressed), and my home directory consisting largely of text files
+(about 1.3 Gbytes uncompressed).  Tests were run on a single core
+and a dual core system.
+
+Dual Core (AMDx2 3800+) system:
+Source directories on ext3.
+
+LiveCD, old mksquashfs:
+
+real    11m48.401s
+user    9m27.056s
+sys     0m15.281s
+
+LiveCD, new par_mksquashfs:
+
+real    4m8.736s
+user    7m11.771s
+sys     0m27.749s
+
+"Home", old mksquashfs:
+
+real    4m34.360s
+user    3m54.007s
+sys     0m32.155s
+
+"Home", new par_mksquashfs:
+
+real    1m27.381s
+user    2m7.304s
+sys     0m17.234s
+
+Single Core PowerBook (PowerPC G4 1.5 GHz Ubuntu Linux)
+Source directories on ext3.
+
+LiveCD, old mksquashs:
+
+real    11m38.472s
+user    9m6.137s
+sys     0m23.799s
+
+LiveCD,  par_mksquashfs:
+
+real    10m5.572s
+user    8m59.921s
+sys     0m16.145s
+
+"Home", old mksquashfs:
+
+real    3m42.298s
+user    2m49.478s
+sys     0m13.675s
+
+"Home", new par_mksquashfs:
+
+real    3m9.178s
+user    2m50.699s
+sys     0m9.069s
+
+I'll be interested in any performance results obtained, especially from SMP
+machines larger than my dual-core AMD box, as this will give an indication of
+the scalability of the code.  Obviously, I'm also interested in any problems,
+deadlocks, low performance etc.
+
+2. UNSQUASHFS
+-------------
+
+Unsquashfs now allows you to specify the filename or directory that is to be
+extracted from the Squashfs filesystem, rather than always extracting the
+entire filesystem.  It also has a new "-force" option, and all options can be
+specified in a short form (-i rather than -info).
+
+The Unsquashfs usage info is now:
+
+SYNTAX: ./unsquashfs [options] filesystem [directory or file to extract]
+       -v[ersion]              print version, licence and copyright information
+       -i[nfo]                 print files as they are unsquashed
+       -l[s]                   list filesystem only
+       -d[est] <pathname>      unsquash to <pathname>, default "squashfs-root"
+       -f[orce]                if file already exists then overwrite
+
+To extract a subset of the filesystem, the filename or directory
+tree that is to be extracted can now be specified on the command line.  The
+file/directory should be specified using the full path to the file/directory
+as it appears within the Squashfs filesystem.  The file/directory will also be
+extracted to that position within the specified destination directory.
+
+The new "-force" option forces Unsquashfs to output to the destination
+directory even if files or directories already exist.  This allows you
+to update an existing directory tree, or to Unsquashfs to a partially
+filled directory.  Without the "-force" option, Unsquashfs will
+refuse to overwrite any existing files, or to create any directories if they
+already exist.  This is done to protect data in case of mistakes, and
+so the "-force" option should be used with caution.
diff --git a/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-3.2 b/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-3.2
new file mode 100644 (file)
index 0000000..e38286f
--- /dev/null
@@ -0,0 +1,33 @@
+       SQUASHFS 3.2 - A squashed read-only filesystem for Linux
+
+       Copyright 2002-2007 Phillip Lougher <phillip@lougher.org.uk>
+
+       Released under the GPL licence (version 2 or later).
+
+Welcome to Squashfs version 3.2.  Squashfs 3.2 has support for NFS exporting,
+some improvements to the Squashfs tools (Mksquashfs and Unsquashfs), some
+major bug fixes, new kernel patches, and various other smaller improvements
+and bug fixes.  Please see the CHANGES file for a detailed list.
+
+1. MKSQUASHFS
+-------------
+
+New command line options:
+
+-no-exports
+
+       Squashfs now supports NFS exports.  By default the additional
+       information necessary is added to the filesystem by Mksquashfs.  If you
+       do not wish this extra information, then this option can be specified.
+       This will save a couple of bytes per file, and the filesystem
+       will be identical to Squashfs 3.1.
+
+-no-progress
+
+       Mksquashfs by default now displays a progress bar. This option disables
+       it.
+
+2. UNSQUASHFS
+-------------
+
+       Unsquashfs now supports Squashfs 2.x filesystems.
diff --git a/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-3.3 b/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-3.3
new file mode 100644 (file)
index 0000000..a38a39e
--- /dev/null
@@ -0,0 +1,169 @@
+       SQUASHFS 3.3 - A squashed read-only filesystem for Linux
+
+       Copyright 2002-2007 Phillip Lougher <phillip@lougher.demon.co.uk>
+
+       Released under the GPL licence (version 2 or later).
+
+Welcome to another release of Squashfs.  This is the 22nd release in just
+over five years of work.  Squashfs 3.3 has lots of nice improvements,
+both to the filesystem itself (bigger blocks, and sparse files), but
+also to the Squashfs-tools Mksquashfs and Unsquashfs.  As usual the
+CHANGES file has a detailed list of all the improvements.
+
+Following is a description of the changes to  the Squashfs tools, usage
+guides to the new options, and a summary of the new options.
+
+1. MKSQUASHFS - EXTENDED EXCLUDE FILE HANDLING
+----------------------------------------------
+
+1. Extended wildcard pattern matching now supported in exclude files
+
+  Enabled by specifying -wildcards option
+
+  Supports both anchored and non-anchored exclude files.
+
+1.1 Anchored excludes
+
+  Similar to existing exclude files except with wildcards.  Exclude
+  file matches from root of source directories.
+
+  Examples:
+
+  1. mksquashfs example image.sqsh -wildcards -e 'test/*.gz'
+
+     Exclude all files matching "*.gz" in the top level directory "test".
+
+  2. mksquashfs example image.sqsh -wildcards -e '*/[Tt]est/example*'
+
+     Exclude all files beginning with "example" inside directories called
+     "Test" or "test", that occur inside any top level directory.
+
+  Using extended wildcards, negative matching is also possible.
+
+  3. mksquashfs example image.sqsh -wildcards -e 'test/!(*data*).gz'
+
+     Exclude all files matching "*.gz" in top level directory "test",
+     except those with "data" in the name.
+
+1.2 Non-anchored excludes
+
+  By default excludes match from the top level directory, but it is
+  often useful to exclude a file matching anywhere in the source directories.
+  For this non-anchored excludes can be used, specified by pre-fixing the
+  exclude with "...".
+
+  Examples:
+
+  1. mksquashfs example image.sqsh -wildcards -e '... *.gz'
+
+     Exclude files matching "*.gz" anywhere in the source directories.
+     For example this will match "example.gz", "test/example.gz", and
+     "test/test/example.gz".
+
+  2. mksquashfs example image.sqsh -wildcards -e '... [Tt]est/*.gz'
+
+     Exclude files matching "*.gz" inside directories called "Test" or
+     "test" that occur anywhere in the source directories.
+
+  Again, using extended wildcards, negative matching is also possible.
+
+  3. mksquashfs example image.sqsh -wildcards -e '... !(*data*).gz'
+
+     Exclude all files matching "*.gz" anywhere in the source directories,
+     except those with "data" in the name.
+
+2. Regular expression pattern matching now supported in exclude files
+
+  Enabled by specifying -regex option.  Identical behaviour to wild
+card pattern matching, except patterns are considered to be regular
+expressions.
+
+  Supports both anchored and non-anchored exclude files.
+
+
+2. MKSQUASHFS - NEW RECOVERY FILE FEATURE
+-----------------------------------------
+
+Recovery files are now created when appending to existing Squashfs
+filesystems.  This allows the original filesystem to be recovered
+if Mksquashfs aborts unexpectedly (i.e. power failure).
+
+The recovery files are called squashfs_recovery_xxx_yyy, where
+"xxx" is the name of the filesystem being appended to, and "yyy" is a
+number to guarantee filename uniqueness (the PID of the parent Mksquashfs
+process).
+
+Normally if Mksquashfs exits correctly the recovery file is deleted to
+avoid cluttering the filesystem.  If Mksquashfs aborts, the "-recover"
+option can be used to recover the filesystem, giving the previously
+created recovery file as a parameter, i.e.
+
+mksquashfs dummy image.sqsh -recover squashfs_recovery_image.sqsh_1234
+
+The writing of the recovery file can be disabled by specifying the
+"-no-recovery" option.
+
+
+3. UNSQUASHFS - EXTENDED EXTRACT FILE HANDLING
+----------------------------------------------
+
+1. Multiple extract files can now be specified on the command line, and the
+files/directories to be extracted can now also be given in a file.
+
+To specify a file containing the extract files use the "-e[f]" option.
+
+2. Extended wildcard pattern matching now supported in extract files
+
+  Enabled by default.  Similar to existing extract files except with
+wildcards.
+
+  Examples:
+
+  1. unsquashfs image.sqsh 'test/*.gz'
+
+     Extract all files matching "*.gz" in the top level directory "test".
+
+  2. unsquashfs image.sqsh '[Tt]est/example*'
+
+     Extract all files beginning with "example" inside top level directories
+     called "Test" or "test".
+
+  Using extended wildcards, negative matching is also possible.
+
+  3. unsquashfs image.sqsh 'test/!(*data*).gz'
+
+     Extract all files matching "*.gz" in top level directory "test",
+     except those with "data" in the name.
+
+3. Regular expression pattern matching now supported in extract files
+
+  Enabled by specifying -r[egex] option.  Identical behaviour to wild
+card pattern matching, except patterns are considered to be regular
+expressions.
+
+4. UNSQUASHFS - EXTENDED FILENAME PRINTING
+------------------------------------------
+
+Filename printing has been enhanced and Unquashfs can now display filenames
+with file attributes ('ls -l' style output).
+
+New options:
+
+  -ll[s]
+
+   list filesystem with file attributes, but don't unsquash
+
+  -li[nfo]
+
+   print files as they are unsquashed with file attributes
+
+
+5. UNSQUASHFS - MISCELLANEOUS OPTIONS
+-------------------------------------
+
+   -s[tat]
+
+   Display the filesystem superblock information.  This is useful to
+   discover the filesystem version, byte ordering, whether it has an
+   NFS export table, and what options were used to compress
+   the filesystem.
diff --git a/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-4.0 b/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-4.0
new file mode 100644 (file)
index 0000000..8cc9514
--- /dev/null
@@ -0,0 +1,48 @@
+       SQUASHFS 4.0 - A squashed read-only filesystem for Linux
+
+       Copyright 2002-2009 Phillip Lougher <phillip@lougher.demon.co.uk>
+
+       Released under the GPL licence (version 2 or later).
+
+Welcome to Squashfs 4.0.  This is an initial tools only release to
+support users of the 2.6.29 kernel, following the mainlining of Squashfs
+earlier this year.
+
+Later releases will probably contain kernel patches supporting 4.0
+layouts for earlier kernels.
+
+New Mksquashfs options
+----------------------
+
+Mksquashfs now supports pseudo files, these allow fake directories, character
+and block devices to be specified and added to the Squashfs filesystem being
+built, rather than requiring them to be present in the source directories.
+This, for example, allows device nodes to be added to the filesystem without
+requiring root access.
+
+Two options are supported, -p allows one pseudo file to be specified on the
+command line, and -pf allows a pseudo file to be specified containing a
+list of pseduo definitions, one per line.
+
+Pseudo device nodes are specified using 7 arguments
+
+Filename type mode uid gid major minor
+
+Where type is either
+       b - for block devices, and
+       c - for character devices
+
+mode is the octal mode specifier, similar to that expected by chmod.
+
+Uid and gid can be either specified as a decimal number, or by name.
+
+For example:
+
+/dev/chr_dev c 666 root root 100 1
+/dev/blk_dev b 444 0 0 200 200
+
+Directories are specified using 5 arguments
+
+Filename type mode uid gid
+
+Where type is d.
diff --git a/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-4.1 b/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-4.1
new file mode 100644 (file)
index 0000000..d2712f9
--- /dev/null
@@ -0,0 +1,265 @@
+       SQUASHFS 4.1 - A squashed read-only filesystem for Linux
+
+       Copyright 2002-2010 Phillip Lougher <phillip@lougher.demon.co.uk>
+
+       Released under the GPL licence (version 2 or later).
+
+Welcome to Squashfs 4.1.  This is a tools only release, support for Squashfs
+file systems is in mainline (2.6.29 and later).
+
+New features in Squashfs-tools 4.1
+----------------------------------
+
+  1. Support for extended attributes
+  2. Support for LZMA and LZO compression
+  3. New pseudo file features
+
+Compatiblity
+------------
+
+Mksquashfs 4.1 generates 4.0 filesystems.  These filesystems are fully
+compatible/interchangable with filesystems generated by Mksquashfs 4.0 and are
+mountable on 2.6.29 and later kernels.
+
+Extended attributes (xattrs)
+----------------------------
+
+Squashfs file systems now have extended attribute support.  The
+extended attribute implementation has the following features:
+
+1. Layout can store up to 2^48 bytes of compressed xattr data.
+2. Number of xattrs per inode unlimited.
+3. Total size of xattr data per inode 2^48 bytes of compressed data.
+4. Up to 4 Gbytes of data per xattr value.
+5. Inline and out-of-line xattr values supported for higher performance
+   in xattr scanning (listxattr & getxattr), and to allow xattr value
+   de-duplication.
+6. Both whole inode xattr duplicate detection and individual xattr value
+   duplicate detection supported.  These can obviously nest, file C's
+   xattrs can be a complete duplicate of file B, and file B's xattrs
+   can be a partial duplicate of file A.
+7. Xattr name prefix types stored, allowing the redundant "user.", "trusted."
+   etc. characters to be eliminated and more concisely stored.
+8. Support for files, directories, symbolic links, device nodes, fifos
+   and sockets.
+
+Extended attribute support is in 2.6.35 and later kernels.  File systems
+with extended attributes can be mounted on 2.6.29 and later kernels, the
+extended attributes will be ignored with a warning.
+
+LZMA and LZO compression
+------------------------
+
+Squashfs now supports LZMA and LZO compression.
+
+LZO support is in 2.6.36 and newer kernels.  LZMA is not yet in mainline.
+
+New Mksquashfs options
+----------------------
+
+-comp <comp>
+
+    Select <comp> compression.  
+
+    The compression algorithms supported by the build of Mksquashfs can be
+    found by typing mksquashfs without any arguments.  The compressors available
+    are displayed at the end of the help message, e.g.
+
+    Compressors available:
+       gzip (default)
+       lzma
+       lzo
+
+    The default compression used when -comp isn't specified on the command line
+    is indicated by "(default)".
+
+-no-xattrs
+    Don't store extended attributes
+
+-xattrs
+    Store extended attributes
+
+    The default behaviour of Mksquashfs with respect to extended attribute
+    storage is build time selectable.  The Mksquashfs help message indicates
+    whether extended attributes are stored or not, e.g.
+
+       -no-xattrs              don't store extended attributes
+       -xattrs                 store extended attributes (default)
+
+    shows that extended attributes are stored by default, and can be disabled
+    by the -no-xattrs option.
+
+       -no-xattrs              don't store extended attributes (default)
+       -xattrs                 store extended attributes 
+
+    shows that extended attributes are not stored by default, storage can be
+    enabled by the -xattrs option.
+
+
+-noX
+-noXattrCompression
+    Don't compress extended attributes
+
+
+New Unsquashfs options
+----------------------
+
+-n[o-xattrs]
+    Don't extract xattrs in filesystem
+
+-x[attrs]
+    Extract xattrs in filesystem
+
+    The default behaviour of Unsquashfs with respect to extended attributes
+    is build time selectable.  The Unsquashfs help message indicates whether
+    extended attributes are stored or not, e.g.
+
+       -no[-xattrs]            don't extract xattrs in file system
+       -x[attrs]               extract xattrs in file system (default)
+
+    shows that xattrs are extracted by default.
+
+       -no[-xattrs]            don't extract xattrs in file system (default)
+       -x[attrs]               extract xattrs in file system
+
+    shows that xattrs are not extracted by default.
+
+
+New pseudo file support
+-----------------------
+
+Mksquashfs supports pseudo files, these allow fake files, directories, character
+and block devices to be specified and added to the Squashfs filesystem being
+built, rather than requiring them to be present in the source directories.
+This, for example, allows device nodes to be added to the filesystem without
+requiring root access.
+
+Mksquashfs 4.1 adds support for "dynamic pseudo files" and a modify operation.
+Dynamic pseudo files allow files to be dynamically created when Mksquashfs
+is run, their contents being the result of running a command or piece of
+shell script.  The modifiy operation allows the mode/uid/gid of an existing
+file in the source filesystem to be modified.
+
+Two Mksquashfs options are supported, -p allows one pseudo file to be specified
+on the command line, and -pf allows a pseudo file to be specified containing a
+list of pseduo definitions, one per line.
+
+Pseudo operations
+-----------------
+
+1. Creating a dynamic file
+--------------------------
+
+Pseudo definition
+
+Filename f mode uid gid command
+
+mode is the octal mode specifier, similar to that expected by chmod.
+
+uid and gid can be either specified as a decimal number, or by name.
+
+command can be an executable or a piece of shell script, and it is executed
+by running "/bin/sh -c command".   The stdout becomes the contents of
+"Filename".
+
+Examples:
+
+Running a basic command
+-----------------------
+
+/somedir/dmesg f 444 root root dmesg
+
+creates a file "/somedir/dmesg" containing the output from dmesg.
+
+Executing shell script
+----------------------
+
+RELEASE f 444 root root \
+               if [ ! -e /tmp/ver ]; then \
+                       echo 0 > /tmp/ver; \
+               fi; \
+                ver=`cat /tmp/ver`; \
+                ver=$((ver +1)); \
+                echo $ver > /tmp/ver; \
+                echo -n `cat /tmp/release`; \
+                echo "-dev #"$ver `date` "Build host" `hostname`
+
+Creates a file RELEASE containing the release name, date, build host, and
+an incrementing version number.  The incrementing version is a side-effect
+of executing the shell script, and ensures every time Mksquashfs is run a
+new version number is used without requiring any other shell scripting.
+
+The above example also shows that commands can be split across multiple lines
+using "\".  Obviously as the script will be presented to the shell as a single
+line, a semicolon is need to separate individual shell commands within the
+shell script.
+
+Reading from a device (or fifo/named socket)
+--------------------------------------------
+
+input f 444 root root dd if=/dev/sda1 bs=1024 count=10
+
+Copies 10K from the device /dev/sda1 into the file input.  Ordinarily Mksquashfs
+given a device, fifo, or named socket will place that special file within the
+Squashfs filesystem, the above allows input from these special files to be
+captured and placed in the Squashfs filesystem.
+
+2. Creating a block or character device
+---------------------------------------
+
+Pseudo definition
+
+Filename type mode uid gid major minor
+
+Where type is either
+       b - for block devices, and
+       c - for character devices
+
+mode is the octal mode specifier, similar to that expected by chmod.
+
+uid and gid can be either specified as a decimal number, or by name.
+
+For example:
+
+/dev/chr_dev c 666 root root 100 1
+/dev/blk_dev b 666 0 0 200 200
+
+creates a character device "/dev/chr_dev" with major:minor 100:1 and
+a block device "/dev/blk_dev" with major:minor 200:200, both with root
+uid/gid and a mode of rw-rw-rw.
+
+3. Creating a directory
+-----------------------
+
+Pseudo definition
+
+Filename d mode uid gid
+
+mode is the octal mode specifier, similar to that expected by chmod.
+
+uid and gid can be either specified as a decimal number, or by name.
+
+For example:
+
+/pseudo_dir d 666 root root
+
+creates a directory "/pseudo_dir" with root uid/gid and mode of rw-rw-rw.
+
+4. Modifying attributes of an existing file
+-------------------------------------------
+
+Pseudo definition
+
+Filename m mode uid gid
+
+mode is the octal mode specifier, similar to that expected by chmod.
+
+uid and gid can be either specified as a decimal number, or by name.
+
+For example:
+
+dmesg m 666 root root
+
+Changes the attributes of the file "dmesg" in the filesystem to have
+root uid/gid and a mode of rw-rw-rw, overriding the attributes obtained
+from the source filesystem.
diff --git a/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-4.2 b/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-4.2
new file mode 100644 (file)
index 0000000..db28f53
--- /dev/null
@@ -0,0 +1,57 @@
+       SQUASHFS 4.2 - A squashed read-only filesystem for Linux
+
+       Copyright 2002-2011 Phillip Lougher <phillip@lougher.demon.co.uk>
+
+       Released under the GPL licence (version 2 or later).
+
+Welcome to Squashfs 4.2.  This is a tools only release, support for Squashfs
+filesystems is in mainline (2.6.29 and later).
+
+New features in Squashfs-tools 4.2
+----------------------------------
+
+  1. Support for XZ compression
+  2. Support for compressor specific options
+
+Compatiblity
+------------
+
+Mksquashfs 4.2 generates 4.0 filesystems.  These filesystems are fully
+compatible/interchangable with filesystems generated by Mksquashfs 4.0 and are
+mountable on 2.6.29 and later kernels.
+
+XZ compression
+--------------
+
+Squashfs now supports XZ compression.  
+
+XZ support is in 2.6.38 and newer kernels.
+
+New Mksquashfs options
+----------------------
+
+-X<compressor-option>
+
+  Compression algorithms can now support compression specific options.  These
+options are prefixed by -X, and are passed to the compressor for handling.
+
+  The compression specific options supported by each compressor can be
+found by typing mksquashfs without any arguments.  They are displayed at the
+end of the help message, e.g. 
+
+Compressors available and compressor specific options:
+       gzip (no options) (default)
+       lzo (no options)
+       xz
+         -Xbcj filter1,filter2,...,filterN
+               Compress using filter1,filter2,...,filterN in turn
+               (in addition to no filter), and choose the best compression.
+               Available filters: x86, arm, armthumb, powerpc, sparc, ia64
+         -Xdict-size <dict-size>
+               Use <dict-size> as the XZ dictionary size.  The dictionary size
+               can be specified as a percentage of the block size, or as an
+               absolute value.  The dictionary size must be less than or equal
+               to the block size and 8192 bytes or larger.  It must also be
+               storable in the xz header as either 2^n or as 2^n+2^(n+1).
+               Example dict-sizes are 75%, 50%, 37.5%, 25%, or 32K, 16K, 8K
+               etc.
diff --git a/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-4.3 b/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/README-4.3
new file mode 100644 (file)
index 0000000..d2370a0
--- /dev/null
@@ -0,0 +1,182 @@
+       SQUASHFS 4.3 - A squashed read-only filesystem for Linux
+
+       Copyright 2002-2014 Phillip Lougher <phillip@lougher.demon.co.uk>
+
+       Released under the GPL licence (version 2 or later).
+
+Welcome to Squashfs 4.3.  This is the first release in over 3 years, and
+there are substantial improvements to stability, new compression options
+and compressors, speed optimisations, and new options for Mksquashfs/Unsquashfs.
+
+This is a tools only release, support for Squashfs filesystems is
+in mainline (2.6.29 and later).
+
+Changes in Squashfs-tools 4.3
+-----------------------------
+
+1. Stability improvements.  Better checking of user input for out of
+   range/invalid values.  Better handling of corrupted Squashfs filesystems
+   (Mksquashfs append mode, and Unsquashfs).  Better handling of buffer
+   overflow/underflow.
+
+2. GZIP compressor now supports compression options, allowing different
+   compression levels to be used.
+
+3. Rewritten LZO compressor with compression options, allowing different
+   LZO algorithms and different compression levels to be used.
+
+4. New LZ4 compressor (note not yet in mainline kernel)
+
+5. Better default memory usage for Mksquashfs.  Mksquashfs by default now
+   uses 25% of physical memory.
+
+6. Duplicate checking in Mksquashfs further optimised.  With certain
+   "problem filesystems" greater than 2x performance improvement.
+   Filesystems with a lot of duplicates should see at least 10-20% speed
+   improvement.
+
+7. The -stat option in Unsquashfs now displays the compression options
+   used to generate the original filesystem.  Previously -stat only displayed
+   the compression algorithm used.
+
+8. The file being compressed/uncompressed in Mksquashfs/Unsquashfs is now
+   displayed if CTRL-\ (SIGQUIT from keyboard) typed.
+
+9. The status of the internal queues/caches in Mksquashfs/Unsquashfs is
+   now displayed if CTRL-\ (SIGQUIT from keyboard) is typed twice within
+   one second.  Normally only useful for "power users", but it can be
+   used to discover if there's any bottlenecks affecting performance
+   (the bottleneck will normally be the compressors/fragment compressors).
+
+10. Miscellaneous new options for Mksquashfs/Unsquashfs to fine tune behaviour.
+
+11. Fixes for CVE-2012-4024 and CVE-2012-4025.
+
+Compatiblity
+------------
+
+Mksquashfs 4.3 generates 4.0 filesystems.  These filesystems are fully
+compatible/interchangable with filesystems generated by Mksquashfs 4.0 and are
+mountable on 2.6.29 and later kernels.
+
+Compressors
+-----------
+
+New compression options and compressors are now supported.
+
+The new options and compressors are:
+
+1. gzip
+         -Xcompression-level <compression-level>
+               <compression-level> should be 1 .. 9 (default 9)
+         -Xwindow-size <window-size>
+               <window-size> should be 8 .. 15 (default 15)
+         -Xstrategy strategy1,strategy2,...,strategyN
+               Compress using strategy1,strategy2,...,strategyN in turn
+               and choose the best compression.
+               Available strategies: default, filtered, huffman_only,
+               run_length_encoded and fixed
+
+2. lzo
+         -Xalgorithm <algorithm>
+               Where <algorithm> is one of:
+                       lzo1x_1
+                       lzo1x_1_11
+                       lzo1x_1_12
+                       lzo1x_1_15
+                       lzo1x_999 (default)
+         -Xcompression-level <compression-level>
+               <compression-level> should be 1 .. 9 (default 8)
+               Only applies to lzo1x_999 algorithm
+
+3. lz4
+         -Xhc
+               Compress using LZ4 High Compression
+
+The compression specific options are, obviously, specific to the compressor
+in question, and you should read the compressor documentation and check
+their web sites to understand their behaviour.
+
+In general the defaults used by Mksquashfs for each compressor are optimised
+to give the best performance for each compressor, where what constitutes
+best depends on the compressor.  For gzip/xz best means highest compression
+(trying multiple filters/strategies can improve compression, but this is
+extremely expensive computationally, and hence, not suitable for the defaults),
+for LZO/LZ4 best means a tradeoff between compression and (de)-compression
+overhead (LZO/LZ4 by definition are intended for weaker processors).
+
+New Mksquashfs options
+----------------------
+
+1. -mem <size>
+
+  Set the amount of memory used by Mksquashfs to <size> bytes.  G/M and K
+  post-fixes are supported.
+
+  By default Mksquashfs uses 25% of the physical memory.  Increasing
+  this with the -mem option can increase performance (note it does not have
+  any effect on compression).  Reducing it can prevent thrashing if the
+  system is busy and there is not 25% of physical memory free (again, note
+  it does not have any effect on compression).
+
+2. -exit-on-error
+
+  By default Mksquashfs treats certain errors as benign, if these
+  errors occur Mksquashfs prints the error on the console but continues.
+  These errors are typically failure to read a file from the source filesystem.
+  This is deliberate, in many cases users prefer Mksquashfs to flag
+  the error but continue rather than abort what may be hours of compression.
+
+  But there are times where failure to read any file is considered critical,
+  and users (especially in the case of automated scripts where the
+  errors output to the console may be missed) prefer Mksquashfs to exit.
+
+  The new -exit-on-error option can be used in this scenario.  This option
+  makes Mksquashfs treat all benign errors as fatal.
+
+3. -progress
+
+  By default if -info is specified, the progress bar is disabled as it gets
+  in the way.  Occasionally you might want the progress bar enabled whilst
+  -info is enabled.  This option forces Mksquashfs to output the progress
+  bar when -info is specified.
+
+4. -Xhelp
+
+  Display the usage text for the currently selected compressor.
+
+New Unsquashfs options
+----------------------
+
+1. -u[ser-xattrs]
+
+  Only write user xattrs.  This forces Unsquashfs to ignore system xattrs.
+  This is useful when Unsquashing a filesystem as a non-root user, and the
+  filesystem contains system xattrs which are only writable by root.
+
+Major bugs fixed
+----------------
+
+1. If Mksquashfs ran out of space in the destination filesystem, this
+   would not cause Mksquashfs to immediately abort, and Mksquashfs would
+   continue to process the source filesystem.  Mksquashfs now immediately
+   aborts on out of space in the destination filesystem.
+
+2. Unsquashfs ignored the maximum number of open files limit, and if that
+   was lower than the default limit for Linux, it would run out of file
+   descriptors.  Unsquashfs now limits the number of open files to the
+   limit currently in force (e.g. specified by setrlimit).
+
+3. If huge numbers of dynamic pseudo files were specified, Mksquashfs
+   could exceed the maximum number of open files limit.  This was because
+   Mksquashfs created all the dynamic file processes up front before
+   commencing source filesystem reading and compression.  Mksquashfs
+   now creates the dynamic file processes on demand whilst reading
+   and compressing the source filesystem, thus limiting the number of
+   dynamic pseudo file processes in existence at any one time.
+
+4. When outputting Unsquashfs used to set the permissions of directories
+   as it recursively descended.  This in hindsight had an obvious oversight,
+   if a directory had only read permission (or was otherwise restricted), then
+   Unsquashfs would fail to write its contents when descending into it.  Fixed
+   by setting directory permissions as Unsquashfs recursively unwinds.
diff --git a/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/pseudo-file.example b/SQUASHFS/squashfs-tools-4.4/RELEASE-READMEs/pseudo-file.example
new file mode 100644 (file)
index 0000000..f866d90
--- /dev/null
@@ -0,0 +1,74 @@
+# Pseudo file example
+
+# Mksquashfs supports pseudo files, these allow fake files, directories,
+# character and block devices to be specified and added to the Squashfs
+# filesystem being built, rather than requiring them to be present in the
+# source directories.
+#
+# This, for example, allows device nodes to be added to the filesystem without
+# requiring root access.
+
+# Mksquashfs 4.1 adds support for "dynamic pseudo files" and a modify operation.
+# Dynamic pseudo files allow files to be dynamically created when Mksquashfs
+# is run, their contents being the result of running a command or piece of
+# shell script.  The modifiy operation allows the mode/uid/gid of an existing
+# file in the source filesystem to be modified.
+
+# Two Mksquashfs options are supported, -p allows one pseudo file to be
+# specified #on the command line, and -pf allows a pseudo file to be specified
+# containing a list of pseduo definitions, one per line.
+
+# Pseudo file examples
+# Run mkquashfs . /tmp/img -pf pseudo-file.examples
+# to see their effect
+
+# Creating dynamic file examples
+
+# Create a file "dmesg" containing the output from dmesg.
+dmesg f 444 root root dmesg
+
+
+# Create a file RELEASE containing the release name, date, build host, and
+# an incrementing version number.  The incrementing version is a side-effect
+# of executing the shell script, and ensures every time Mksquashfs is run a
+# new version number is used without requiring any other shell scripting.
+RELEASE f 444 root root \
+               if [ ! -e /tmp/ver ]; then \
+                       echo 0 > /tmp/ver; \
+               fi; \
+                ver=`cat /tmp/ver`; \
+                ver=$((ver +1)); \
+                echo $ver > /tmp/ver; \
+                echo -n "release x.x"; \
+                echo "-dev #"$ver `date` "Build host" `hostname`
+
+
+# Copy 10K from the device /dev/sda1 into the file input.  Ordinarily
+# Mksquashfs given a device, fifo, or named socket will place that special file
+# within the Squashfs filesystem, this allows input from these special
+# files to be captured and placed in the Squashfs filesystem.
+input f 444 root root dd if=/dev/sda1 bs=1024 count=10
+
+
+# Creating a block or character device examples
+
+# Create a character device "chr_dev" with major:minor 100:1 and
+# a block device "blk_dev" with major:minor 200:200, both with root
+# uid/gid and a mode of rw-rw-rw.
+chr_dev c 666 root root 100 1
+blk_dev b 666 0 0 200 200
+
+
+# Creating a directory example
+
+# create a directory "pseudo_dir" with root uid/gid and mode of r--r--r--.
+pseudo_dir d 444 root root
+
+
+# Modifying attributes of an existing file exmaple
+
+# Change the attributes of the file "INSTALL" in the filesystem to have
+# root uid/gid and a mode of rw-rw-rw, overriding the attributes obtained
+# from the source filesystem.
+INSTALL m 666 root root
+
diff --git a/SQUASHFS/squashfs-tools-4.4/USAGE b/SQUASHFS/squashfs-tools-4.4/USAGE
new file mode 100644 (file)
index 0000000..3cf66b3
--- /dev/null
@@ -0,0 +1,1216 @@
+       SQUASHFS 4.4 - A squashed read-only filesystem for Linux
+
+       Copyright 2002-2019 Phillip Lougher <phillip@squashfs.org.uk>
+
+       Released under the GPL licence (version 2 or later).
+
+Welcome to Squashfs version 4.4.  Please read the README-4.3 and CHANGES files
+for details of changes.
+
+Squashfs is a highly compressed read-only filesystem for Linux.
+It uses either gzip/xz/lzo/lz4/zstd 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 1Mbytes (default block size 128K).
+
+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 constrained
+block device/memory systems (e.g. embedded systems) where low overhead is
+needed.
+
+1. SQUASHFS OVERVIEW
+--------------------
+
+1. Data, inodes and directories are compressed.
+
+2. Squashfs stores full uid/gids (32 bits), and file creation time.
+
+3. In theory files up to 2^64 bytes are supported.  In theory filesystems can
+   be up to 2^64 bytes.
+
+4. Inode and directory data are highly compacted, and packed on byte
+   boundaries.  Each compressed inode is on average 8 bytes in length
+   (the exact length varies on file type, i.e. regular file, directory,
+   symbolic link, and block/char device inodes have different sizes).
+
+5. Squashfs can use block sizes up to 1Mbyte (the default size is 128K).
+   Using 128K blocks achieves greater compression ratios than the normal
+   4K block size.
+
+6. File duplicates are detected and removed.
+
+7. Filesystems can be compressed with gzip, xz (lzma2), lzo, lz4
+   or zstd compression algorithms.
+
+
+1.1 Introducing reproducible builds
+-----------------------------------
+
+Ever since Mksquashfs was parallelised back in 2006, there
+has been a certain randomness in how fragments and multi-block
+files are ordered in the output filesystem even if the input
+remains the same.
+
+This is because the multiple parallel threads can be scheduled
+differently between Mksquashfs runs.  For example, the thread
+given fragment 10 to compress may finish before the thread
+given fragment 9 to compress on one run (writing fragment 10
+to the output filesystem before fragment 9), but, on the next
+run it could be vice-versa.  There are many different scheduling
+scenarios here, all of which can have a knock on effect causing
+different scheduling and ordering later in the filesystem too.
+
+Mkquashfs doesn't care about the ordering of fragments and
+multi-block files within the filesystem, as this does not
+affect the correctness of the filesystem.
+
+In fact not caring about the ordering, as it doesn't matter, allows
+Mksquashfs to run as fast as possible, maximising CPU and I/O
+performance.
+
+But, in the last couple of years, Squashfs has become used in
+scenarios (cloud etc) where this randomness is causing problems.
+Specifically this appears to be where downloaders, installers etc.
+try to work out the differences between Squashfs filesystem
+updates to minimise the amount of data that needs to transferred
+to update an image.
+
+Additionally, in the last couple of years has arisen the notion
+of reproducible builds, that is the same source and build
+environment etc should be able to (re-)generate identical
+output.  This is usually for verification and security, allowing
+binaries/distributions to be checked for malicious activity.
+See https://reproducible-builds.org/ for more information.
+
+Mksquashfs from release 4.4 now generates reproducible images
+by default.  Images generated by Mksquashfs will be ordered
+identically to previous runs if the same input has been supplied,
+and the same options used.
+
+1.1.1 Dealing with timestamps
+
+Timestamps embedded in the filesystem will stiil cause differences.
+Each new run of Mksquashfs will produce a different mkfs (make filesystem)
+timestamp in the super-block.  Moreover if any file timestamps have changed
+(even if the content hasn't), this will produce a difference.
+
+To prevent timestamps from producing differences, the following
+new Mksquashfs options have been added.
+
+1.1.2 -mkfs-time <time>
+
+This option takes a positive time value (which is the number
+of seconds since the epoch of 1970-01-01 00:00:00 UTC), and sets
+the file system timestamp to that.
+
+Squashfs uses an unsigned 32-bit integer to store time, and the
+time given should be in that range.
+
+Obviously you can use the date command to convert dates into
+this value, i.e.
+
+% mksquashfs source source.sqsh -mkfs-time $(date +%s -d "Jan 1 2019 19:00")
+
+1.1.3 -all-time <time>
+
+This option takes a positive time value (which is the number
+of seconds since the epoch of 1970-01-01 00:00:00 UTC), and sets
+the timestamp on all files to that (but not the mkfs time).
+
+1.1.4 environment variable SOURCE_DATE_EPOCH
+
+As an alternative to the above command line options, you can
+set the environment variable SOURCE_DATE_EPOCH to a time value.
+
+This value will be used to set the mkfs time.  Also any
+file timestamps which are after SOURCE_DATE_EPOCH will be
+clamped to SOURCE_DATE_EPOCH.
+
+See https://reproducible-builds.org/docs/source-date-epoch/
+for more information.
+
+Note: both SOURCE_DATE_EPOCH and the command line options cannot
+be used at the same time.  They are different ways to do the same thing,
+and both have FORCE sematics which mean they can't be over-ridden
+elsewhere (otherwise it would defeat the purpose).
+
+1.1.5 -not-reproducible
+
+This option tells Mksquashfs that the files do not have to be
+strictly ordered.  This will make Mksquashfs behave like version 4.3.
+
+1.2 Extended attributes (xattrs)
+--------------------------------
+
+Squashfs filesystems now have extended attribute support.  The
+extended attribute implementation has the following features:
+
+1. Layout can store up to 2^48 bytes of compressed xattr data.
+2. Number of xattrs per inode unlimited.
+3. Total size of xattr data per inode 2^48 bytes of compressed data.
+4. Up to 4 Gbytes of data per xattr value.
+5. Inline and out-of-line xattr values supported for higher performance
+   in xattr scanning (listxattr & getxattr), and to allow xattr value
+   de-duplication.
+6. Both whole inode xattr duplicate detection and individual xattr value
+   duplicate detection supported.  These can obviously nest, file C's
+   xattrs can be a complete duplicate of file B, and file B's xattrs
+   can be a partial duplicate of file A.
+7. Xattr name prefix types stored, allowing the redundant "user.", "trusted."
+   etc. characters to be eliminated and more concisely stored.
+8. Support for files, directories, symbolic links, device nodes, fifos
+   and sockets.
+
+Extended attribute support is in 2.6.35 and later kernels.  Filesystems
+with extended attributes can be mounted on 2.6.29 and later kernels, the
+extended attributes will be ignored with a warning.
+
+2. USING SQUASHFS
+-----------------
+
+Squashfs filesystems should be mounted with 'mount' with the filesystem type
+'squashfs'.  If the filesystem is on a block device, the filesystem can be
+mounted directly, e.g.
+
+%mount -t squashfs /dev/sda1 /mnt
+
+Will mount the squashfs filesystem on "/dev/sda1" under the directory "/mnt".
+
+If the squashfs filesystem has been written to a file, the loopback device
+can be used to mount it (loopback support must be in the kernel), e.g.
+
+%mount -t squashfs image /mnt -o loop
+
+Will mount the squashfs filesystem in the file "image" under
+the directory "/mnt".
+
+3. MKSQUASHFS
+-------------
+
+3.1 Mksquashfs options and overview
+-----------------------------------
+
+As squashfs is a read-only filesystem, the mksquashfs program must be used to
+create populated squashfs filesystems.
+
+SYNTAX:mksquashfs source1 source2 ...  dest [options] [-e list of exclude
+dirs/files]
+
+Filesystem build options:
+-comp <comp>           select <comp> compression
+                       Compressors available:
+                               gzip (default)
+                               lzo
+                               lz4
+                               xz
+                               zstd
+-b <block_size>                set data block to <block_size>.  Default 128 Kbytes
+                       Optionally a suffix of K or M can be given to specify
+                       Kbytes or Mbytes respectively
+-reproducible          build images that are reproducible (default)
+-not-reproducible      build images that are not reproducible
+-mkfs-time <time>      set mkfs time to <time> which is an unsigned int
+-fstime <time>         synonym for mkfs-time
+-all-time <time>       set all inode times to <time> which is an unsigned int
+-no-exports            don't make the filesystem exportable via NFS
+-no-sparse             don't detect sparse files
+-no-xattrs             don't store extended attributes
+-xattrs                        store extended attributes (default)
+-noI                   do not compress inode table
+-noId                  do not compress the uid/gid table (implied by -noI)
+-noD                   do not compress data blocks
+-noF                   do not compress fragment blocks
+-noX                   do not compress extended attributes
+-no-fragments          do not use fragments
+-always-use-fragments  use fragment blocks for files larger than block size
+-no-duplicates         do not perform duplicate checking
+-all-root              make all files owned by root
+-root-mode <mode>      set root directory permissions to octal <mode>
+-force-uid <uid>       set all file uids to <uid>
+-force-gid <gid>       set all file gids to <gid>
+-nopad                 do not pad filesystem to a multiple of 4K
+-keep-as-directory     if one source directory is specified, create a root
+                       directory containing that directory, rather than the
+                       contents of the directory
+
+Filesystem filter options:
+-p <pseudo-definition> Add pseudo file definition
+-pf <pseudo-file>      Add list of pseudo file definitions
+                       Pseudo definitions should be of the format
+                               filename d mode uid gid
+                               filename m mode uid gid
+                               filename b mode uid gid major minor
+                               filename c mode uid gid major minor
+                               filename f mode uid gid command
+                               filename s mode uid gid symlink
+-sort <sort_file>      sort files according to priorities in <sort_file>.  One
+                       file or dir with priority per line.  Priority -32768 to
+                       32767, default priority 0
+-ef <exclude_file>     list of exclude dirs/files.  One per line
+-wildcards             Allow extended shell wildcards (globbing) to be used in
+                       exclude dirs/files
+-regex                 Allow POSIX regular expressions to be used in exclude
+                       dirs/files
+
+Filesystem append options:
+-noappend              do not append to existing filesystem
+-root-becomes <name>   when appending source files/directories, make the
+                       original root become a subdirectory in the new root
+                       called <name>, rather than adding the new source items
+                       to the original root
+
+Mksquashfs runtime options:
+-version               print version, licence and copyright message
+-exit-on-error         treat normally ignored errors as fatal
+-recover <name>                recover filesystem data using recovery file <name>
+-no-recovery           don't generate a recovery file
+-quiet                 no verbose output
+-info                  print files written to filesystem
+-no-progress           don't display the progress bar
+-progress              display progress bar when using the -info option
+-processors <number>   Use <number> processors.  By default will use number of
+                       processors available
+-mem <size>            Use <size> physical memory.  Currently set to 4096M
+                       Optionally a suffix of K, M or G can be given to specify
+                       Kbytes, Mbytes or Gbytes respectively
+
+Miscellaneous options:
+-root-owned            alternative name for -all-root
+-offset <offset>       Skip <offset> bytes at the beginning of <dest>.
+                       Optionally a suffix of K, M or G can be given to specify
+                       Kbytes, Mbytes or Gbytes respectively.
+                       Default 0 bytes.
+-o <offset>            synonym for -offset
+-noInodeCompression    alternative name for -noI
+-noIdTableCompression  alternative name for -noId
+-noDataCompression     alternative name for -noD
+-noFragmentCompression alternative name for -noF
+-noXattrCompression    alternative name for -noX
+
+-Xhelp                 print compressor options for selected compressor
+
+Compressors available and compressor specific options:
+       gzip (default)
+         -Xcompression-level <compression-level>
+               <compression-level> should be 1 .. 9 (default 9)
+         -Xwindow-size <window-size>
+               <window-size> should be 8 .. 15 (default 15)
+         -Xstrategy strategy1,strategy2,...,strategyN
+               Compress using strategy1,strategy2,...,strategyN in turn
+               and choose the best compression.
+               Available strategies: default, filtered, huffman_only,
+               run_length_encoded and fixed
+       lzo
+         -Xalgorithm <algorithm>
+               Where <algorithm> is one of:
+                       lzo1x_1
+                       lzo1x_1_11
+                       lzo1x_1_12
+                       lzo1x_1_15
+                       lzo1x_999 (default)
+         -Xcompression-level <compression-level>
+               <compression-level> should be 1 .. 9 (default 8)
+               Only applies to lzo1x_999 algorithm
+       lz4
+         -Xhc
+               Compress using LZ4 High Compression
+       xz
+         -Xbcj filter1,filter2,...,filterN
+               Compress using filter1,filter2,...,filterN in turn
+               (in addition to no filter), and choose the best compression.
+               Available filters: x86, arm, armthumb, powerpc, sparc, ia64
+         -Xdict-size <dict-size>
+               Use <dict-size> as the XZ dictionary size.  The dictionary size
+               can be specified as a percentage of the block size, or as an
+               absolute value.  The dictionary size must be less than or equal
+               to the block size and 8192 bytes or larger.  It must also be
+               storable in the xz header as either 2^n or as 2^n+2^(n+1).
+               Example dict-sizes are 75%, 50%, 37.5%, 25%, or 32K, 16K, 8K
+               etc.
+       zstd
+         -Xcompression-level <compression-level>
+               <compression-level> should be 1 .. 22 (default 15)
+
+Source1 source2 ... are the source directories/files containing the
+files/directories that will form the squashfs filesystem.  If a single
+directory is specified (i.e. mksquashfs source output_fs) the squashfs
+filesystem will consist of that directory, with the top-level root
+directory corresponding to the source directory.
+
+If multiple source directories or files are specified, mksquashfs will merge
+the specified sources into a single filesystem, with the root directory
+containing each of the source files/directories.  The name of each directory
+entry will be the basename of the source path.   If more than one source
+entry maps to the same name, the conflicts are named xxx_1, xxx_2, etc. where
+xxx is the original name.
+
+To make this clear, take two example directories.  Source directory
+"/home/phillip/test" contains  "file1", "file2" and "dir1".
+Source directory "goodies" contains "goodies1", "goodies2" and "goodies3".
+
+usage example 1:
+
+%mksquashfs /home/phillip/test output_fs
+
+This will generate a squashfs filesystem with root entries
+"file1", "file2" and "dir1".
+
+example 2:
+
+%mksquashfs /home/phillip/test goodies output_fs
+
+This will create a squashfs filesystem with the root containing
+entries "test" and "goodies" corresponding to the source
+directories "/home/phillip/test" and "goodies".
+
+example 3:
+
+%mksquashfs /home/phillip/test goodies test output_fs
+
+This is the same as the previous example, except a third
+source directory "test" has been specified.  This conflicts
+with the first directory named "test" and will be renamed "test_1".
+
+Multiple sources allow filesystems to be generated without needing to
+copy all source files into a common directory.  This simplifies creating
+filesystems.
+
+The -keep-as-directory option can be used when only one source directory
+is specified, and you wish the root to contain that directory, rather than
+the contents of the directory.  For example:
+
+example 4:
+
+%mksquashfs /home/phillip/test output_fs -keep-as-directory
+
+This is the same as example 1, except for -keep-as-directory.
+This will generate a root directory containing directory "test",
+rather than the "test" directory contents "file1", "file2" and "dir1".
+
+The Dest argument is the destination where the squashfs filesystem will be
+written.  This can either be a conventional file or a block device.  If the file
+doesn't exist it will be created, if it does exist and a squashfs
+filesystem exists on it, mksquashfs will append.  The -noappend option will
+write a new filesystem irrespective of whether an existing filesystem is
+present.
+
+3.2 Changing compression algorithm and compression specific options
+-------------------------------------------------------------------
+
+By default Mksquashfs will compress using the gzip compression
+algorithm.  This algorithm offers a good trade-off between compression
+ratio, and memory and time taken to decompress.
+
+Squashfs also supports LZ4, LZO and XZ (LZMA2) compression.  LZO offers worse
+compression ratio than gzip, but is faster to decompress.  XZ offers better
+compression ratio than gzip, but at the expense of greater memory and time
+to decompress (and significantly more time to compress).  LZ4 is similar
+to LZO, but, support for it is not yet in the mainline kernel, and so
+its usefulness is currently limited to using Squashfs with Mksquashfs/Unsquashfs
+as an archival system like tar.
+
+If you're not building the squashfs-tools and kernel from source, then
+the tools and kernel may or may not have been built with support for LZ4, LZO or
+XZ compression.  The compression algorithms supported by the build of
+Mksquashfs can be found by typing mksquashfs without any arguments.  The
+compressors available are displayed at the end of the help message, e.g. 
+
+Compressors available and compressor specific options:
+       gzip (default)
+         -Xcompression-level <compression-level>
+               <compression-level> should be 1 .. 9 (default 9)
+         -Xwindow-size <window-size>
+               <window-size> should be 8 .. 15 (default 15)
+         -Xstrategy strategy1,strategy2,...,strategyN
+               Compress using strategy1,strategy2,...,strategyN in turn
+               and choose the best compression.
+               Available strategies: default, filtered, huffman_only,
+               run_length_encoded and fixed
+       lzo
+         -Xalgorithm <algorithm>
+               Where <algorithm> is one of:
+                       lzo1x_1
+                       lzo1x_1_11
+                       lzo1x_1_12
+                       lzo1x_1_15
+                       lzo1x_999 (default)
+         -Xcompression-level <compression-level>
+               <compression-level> should be 1 .. 9 (default 8)
+               Only applies to lzo1x_999 algorithm
+       lz4
+         -Xhc
+               Compress using LZ4 High Compression
+       xz
+         -Xbcj filter1,filter2,...,filterN
+               Compress using filter1,filter2,...,filterN in turn
+               (in addition to no filter), and choose the best compression.
+               Available filters: x86, arm, armthumb, powerpc, sparc, ia64
+         -Xdict-size <dict-size>
+               Use <dict-size> as the XZ dictionary size.  The dictionary size
+               can be specified as a percentage of the block size, or as an
+               absolute value.  The dictionary size must be less than or equal
+               to the block size and 8192 bytes or larger.  It must also be
+               storable in the xz header as either 2^n or as 2^n+2^(n+1).
+               Example dict-sizes are 75%, 50%, 37.5%, 25%, or 32K, 16K, 8K
+               etc.
+       zstd
+         -Xcompression-level <compression-level>
+               <compression-level> should be 1 .. 22 (default 15)
+
+If the compressor offers compression specific options (all the compressors now
+have compression specific options except the deprecated lzma1 compressor)
+then these options are also displayed (.i.e. in the above XZ is shown with two
+compression specific options).  The compression specific options are, obviously,
+specific to the compressor in question, and the compressor documentation and
+web sites should be consulted to understand their behaviour.  In general
+the Mksquashfs compression defaults for each compressor are optimised to
+give the best performance for each compressor, where what constitutes
+best depends on the compressor.  For gzip/xz best means highest compression,
+for LZO/LZ4 best means a tradeoff between compression and (de)-compression
+overhead (LZO/LZ4 by definition are intended for weaker processors).
+
+3.3 Changing global compression defaults used in mksquashfs
+-----------------------------------------------------------
+
+There are a large number of options that can be used to control the 
+compression in mksquashfs.  By and large the defaults are the most
+optimum settings and should only be changed in exceptional circumstances!
+Note, this does not apply to the block size, increasing the block size
+from the default of 128Kbytes will increase compression (especially
+for the xz compressor) and should increase I/O performance too.  However,
+a block size of greater than 128Kbytes may increase latency in certain
+cases (where the filesystem contains lots of fragments, and no locality
+of reference is observed).  For this reason the block size default is
+configured to the less optimal 128Kbytes.  Users should experiment
+with 256Kbyte sizes or above.
+
+The -noI, -noD and -noF options (also -noInodeCompression, -noDataCompression
+and -noFragmentCompression) can be used to force mksquashfs to not compress
+inodes/directories, data and fragments respectively.  Giving all options
+generates an uncompressed filesystem.
+
+The -no-fragments tells mksquashfs to not generate fragment blocks, and rather
+generate a filesystem similar to a Squashfs 1.x filesystem.  It will of course
+still be a Squashfs 4.0 filesystem but without fragments, and so it won't be
+mountable on a Squashfs 1.x system.
+
+The -always-use-fragments option tells mksquashfs to always generate
+fragments for files irrespective of the file length.  By default only small
+files less than the block size are packed into fragment blocks.  The ends of
+files which do not fit fully into a block, are NOT by default packed into
+fragments.  To illustrate this, a 100K file has an initial 64K block and a 36K
+remainder.  This 36K remainder is not packed into a fragment by default.  This
+is because to do so leads to a 10 - 20% drop in sequential I/O performance, as a
+disk head seek is needed to seek to the initial file data and another disk seek
+is need to seek to the fragment block.  Specify this option if you want file
+remainders to be packed into fragment blocks.  Doing so may increase the
+compression obtained BUT at the expense of I/O speed.
+
+The -no-duplicates option tells mksquashfs to not check the files being
+added to the filesystem for duplicates.  This can result in quicker filesystem
+generation and appending although obviously compression will suffer badly if
+there is a lot of duplicate files.
+
+The -b option allows the block size to be selected, both "K" and "M" postfixes
+are supported, this can be either 4K, 8K, 16K, 32K, 64K, 128K, 256K, 512K or
+1M bytes.
+
+3.4 Specifying the UIDs/GIDs used in the filesystem
+---------------------------------------------------
+
+By default files in the generated filesystem inherit the UID and GID ownership
+of the original file.  However,  mksquashfs provides a number of options which
+can be used to override the ownership.
+
+The options -all-root and -root-owned (both do exactly the same thing) force all
+file uids/gids in the generated Squashfs filesystem to be root.  This allows
+root owned filesystems to be built without root access on the host machine.
+
+The "-force-uid uid"  option forces all files in the generated Squashfs
+filesystem to be owned by the specified uid.  The uid can be specified either by
+name (i.e. "root") or by number.
+
+The "-force-gid gid" option forces all files in the generated Squashfs
+filesystem to be group owned by the specified gid.  The gid can be specified
+either by name (i.e. "root") or by number.
+
+3.5 Excluding files from the filesystem
+---------------------------------------
+
+The -e and -ef options allow files/directories to be specified which are
+excluded from the output filesystem.  The -e option takes the exclude
+files/directories from the command line, the -ef option takes the
+exlude files/directories from the specified exclude file, one file/directory
+per line.
+
+Two styles of exclude file matching are supported: basic exclude matching, and
+extended wildcard matching.  Basic exclude matching is a legacy feature
+retained for backwards compatibility with earlier versions of Mksquashfs.
+Extended wildcard matching should be used in preference.
+
+3.5.1 Basic exclude matching
+----------------------------
+
+Each exclude file is treated as an exact match of a file/directory in
+the source directories.  If an exclude file/directory is absolute (i.e.
+prefixed with /, ../, or ./) the entry is treated as absolute, however, if an
+exclude file/directory is relative, it is treated as being relative to each of
+the sources in turn, i.e.
+
+%mksquashfs /tmp/source1 source2  output_fs -e ex1 /tmp/source1/ex2 out/ex3
+
+Will generate exclude files /tmp/source1/ex2, /tmp/source1/ex1, source2/ex1,
+/tmp/source1/out/ex3 and source2/out/ex3.
+
+3.5.2 Extended exclude file handling
+------------------------------------
+
+Extended exclude file matching treats each exclude file as a wildcard or
+regex expression.  To enable wildcard matching specify the -wildcards
+option, and to enable regex matching specify the -regex option.  In most
+cases the -wildcards option should be used rather than -regex because wildcard
+matching behaviour is significantly easier to understand!
+
+In addition to wildcards/regex expressions, exclude files can be "anchored" or
+"non-anchored".  An anchored exclude is one which matches from the root of the
+directory and nowhere else, a non-anchored exclude matches anywhere.  For
+example given the directory hierarchy "a/b/c/a/b", the anchored exclude
+"a/b" will match "a/b" at the root of the directory hierarchy, but
+it will not match the "/a/b" sub-directory within directory "c", whereas a
+non-anchored exclude would.
+
+A couple of examples should make this clearer.
+Anchored excludes
+
+  1. mksquashfs example image.sqsh -wildcards -e 'test/*.gz'
+
+     Exclude all files matching "*.gz" in the top level directory "test".
+
+  2. mksquashfs example image.sqsh -wildcards -e '*/[Tt]est/example*'
+
+     Exclude all files beginning with "example" inside directories called
+     "Test" or "test", that occur inside any top level directory.
+
+  Using extended wildcards, negative matching is also possible.
+
+  3. mksquashfs example image.sqsh -wildcards -e 'test/!(*data*).gz'
+
+     Exclude all files matching "*.gz" in top level directory "test",
+     except those with "data" in the name.
+
+Non-anchored excludes
+
+  By default excludes match from the top level directory, but it is
+  often useful to exclude a file matching anywhere in the source directories.
+  For this non-anchored excludes can be used, specified by pre-fixing the
+  exclude with "...".
+
+  Examples:
+
+  1. mksquashfs example image.sqsh -wildcards -e '... *.gz'
+
+     Exclude files matching "*.gz" anywhere in the source directories.
+     For example this will match "example.gz", "test/example.gz", and
+     "test/test/example.gz".
+
+  2. mksquashfs example image.sqsh -wildcards -e '... [Tt]est/*.gz'
+
+     Exclude files matching "*.gz" inside directories called "Test" or
+     "test" that occur anywhere in the source directories.
+
+  Again, using extended wildcards, negative matching is also possible.
+
+  3. mksquashfs example image.sqsh -wildcards -e '... !(*data*).gz'
+
+     Exclude all files matching "*.gz" anywhere in the source directories,
+     except those with "data" in the name.
+
+3.5.3 Exclude files summary
+---------------------------
+
+The -e and -ef exclude options are usefully used in archiving the entire
+filesystem, where it is wished to avoid archiving /proc, and the filesystem
+being generated, i.e.
+
+%mksquashfs / /tmp/root.sqsh -e proc /tmp/root.sqsh
+
+Multiple -ef options can be specified on the command line, and the -ef
+option can be used in conjuction with the -e option.
+
+3.6 Appending to squashfs filesystems
+-------------------------------------
+
+Running squashfs with the destination directory containing an existing
+filesystem will add the source items to the existing filesystem.  By default,
+the source items are added to the existing root directory.
+
+To make this clear... An existing filesystem "image" contains root entries
+"old1", and "old2".  Source directory "/home/phillip/test" contains  "file1",
+"file2" and "dir1".
+
+example 1:
+
+%mksquashfs /home/phillip/test image
+
+Will create a new "image" with root entries "old1", "old2", "file1", "file2" and
+"dir1"
+
+example 2:
+
+%mksquashfs /home/phillip/test image -keep-as-directory
+
+Will create a new "image" with root entries "old1", "old2", and "test".
+As shown in the previous section, for single source directories
+'-keep-as-directory' adds the source directory rather than the
+contents of the directory.
+
+example 3:
+
+%mksquashfs /home/phillip/test image -keep-as-directory -root-becomes
+original-root
+
+Will create a new "image" with root entries "original-root", and "test".  The
+'-root-becomes' option specifies that the original root becomes a subdirectory
+in the new root, with the specified name.
+
+The append option with file duplicate detection, means squashfs can be
+used as a simple versioning archiving filesystem. A squashfs filesystem can
+be created with for example the linux-2.4.19 source.  Appending the linux-2.4.20
+source will create a filesystem with the two source trees, but only the
+changed files will take extra room, the unchanged files will be detected as
+duplicates.
+
+3.7 Appending recovery file feature
+-----------------------------------
+
+Recovery files are created when appending to existing Squashfs
+filesystems.  This allows the original filesystem to be recovered
+if Mksquashfs aborts unexpectedly (i.e. power failure).
+
+The recovery files are called squashfs_recovery_xxx_yyy, where
+"xxx" is the name of the filesystem being appended to, and "yyy" is a
+number to guarantee filename uniqueness (the PID of the parent Mksquashfs
+process).
+
+Normally if Mksquashfs exits correctly the recovery file is deleted to
+avoid cluttering the filesystem.  If Mksquashfs aborts, the "-recover"
+option can be used to recover the filesystem, giving the previously
+created recovery file as a parameter, i.e.
+
+mksquashfs dummy image.sqsh -recover squashfs_recovery_image.sqsh_1234
+
+The writing of the recovery file can be disabled by specifying the
+"-no-recovery" option.
+
+3.8 Pseudo file support
+-----------------------
+
+Mksquashfs supports pseudo files, these allow fake files, directories, character
+and block devices to be specified and added to the Squashfs filesystem being
+built, rather than requiring them to be present in the source directories.
+This, for example, allows device nodes to be added to the filesystem without
+requiring root access.
+
+Mksquashfs 4.1 added support for "dynamic pseudo files" and a modify operation.
+Dynamic pseudo files allow files to be dynamically created when Mksquashfs
+is run, their contents being the result of running a command or piece of
+shell script.  The modifiy operation allows the mode/uid/gid of an existing
+file in the source filesystem to be modified.
+
+Mksquashfs 4.4 adds support for Symbolic links.
+
+Two Mksquashfs options are supported, -p allows one pseudo file to be specified
+on the command line, and -pf allows a pseudo file to be specified containing a
+list of pseduo definitions, one per line.
+
+3.8.1. Creating a dynamic file
+------------------------------
+
+Pseudo definition
+
+Filename f mode uid gid command
+
+mode is the octal mode specifier, similar to that expected by chmod.
+
+uid and gid can be either specified as a decimal number, or by name.
+
+command can be an executable or a piece of shell script, and it is executed
+by running "/bin/sh -c command".   The stdout becomes the contents of
+"Filename".
+
+Examples:
+
+Running a basic command
+-----------------------
+
+/somedir/dmesg f 444 root root dmesg
+
+creates a file "/somedir/dmesg" containing the output from dmesg.
+
+Executing shell script
+----------------------
+
+RELEASE f 444 root root \
+               if [ ! -e /tmp/ver ]; then \
+                       echo 0 > /tmp/ver; \
+               fi; \
+                ver=`cat /tmp/ver`; \
+                ver=$((ver +1)); \
+                echo $ver > /tmp/ver; \
+                echo -n `cat /tmp/release`; \
+                echo "-dev #"$ver `date` "Build host" `hostname`
+
+Creates a file RELEASE containing the release name, date, build host, and
+an incrementing version number.  The incrementing version is a side-effect
+of executing the shell script, and ensures every time Mksquashfs is run a
+new version number is used without requiring any other shell scripting.
+
+The above example also shows that commands can be split across multiple lines
+using "\".  Obviously as the script will be presented to the shell as a single
+line, a semicolon is need to separate individual shell commands within the
+shell script.
+
+Reading from a device (or fifo/named socket)
+--------------------------------------------
+
+input f 444 root root dd if=/dev/sda1 bs=1024 count=10
+
+Copies 10K from the device /dev/sda1 into the file input.  Ordinarily Mksquashfs
+given a device, fifo, or named socket will place that special file within the
+Squashfs filesystem, the above allows input from these special files to be
+captured and placed in the Squashfs filesystem.
+
+3.8.2. Creating a block or character device
+-------------------------------------------
+
+Pseudo definition
+
+Filename type mode uid gid major minor
+
+Where type is either
+       b - for block devices, and
+       c - for character devices
+
+mode is the octal mode specifier, similar to that expected by chmod.
+
+uid and gid can be either specified as a decimal number, or by name.
+
+For example:
+
+/dev/chr_dev c 666 root root 100 1
+/dev/blk_dev b 666 0 0 200 200
+
+creates a character device "/dev/chr_dev" with major:minor 100:1 and
+a block device "/dev/blk_dev" with major:minor 200:200, both with root
+uid/gid and a mode of rw-rw-rw.
+
+3.8.3. Creating a directory
+---------------------------
+
+Pseudo definition
+
+Filename d mode uid gid
+
+mode is the octal mode specifier, similar to that expected by chmod.
+
+uid and gid can be either specified as a decimal number, or by name.
+
+For example:
+
+/pseudo_dir d 666 root root
+
+creates a directory "/pseudo_dir" with root uid/gid and mode of rw-rw-rw.
+
+3.8.4. Creating a symbolic link
+-------------------------------
+
+Pseudo definition
+
+Filename s mode uid gid symlink
+
+uid and gid can be either specified as a decimal number, or by name.
+
+Note mode is ignored, as symlinks always have "rwxrwxrwx" permissions.
+
+For example:
+
+symlink s 0 root root example
+
+creates a symlink "symlink" to file "example" with root uid/gid.
+
+3.8.5. Modifying attributes of an existing file
+-----------------------------------------------
+
+Pseudo definition
+
+Filename m mode uid gid
+
+mode is the octal mode specifier, similar to that expected by chmod.
+
+uid and gid can be either specified as a decimal number, or by name.
+
+For example:
+
+dmesg m 666 root root
+
+Changes the attributes of the file "dmesg" in the filesystem to have
+root uid/gid and a mode of rw-rw-rw, overriding the attributes obtained
+from the source filesystem.
+
+3.9 Miscellaneous options
+-------------------------
+
+The -info option displays the files/directories as they are compressed and
+added to the filesystem.  The original uncompressed size of each file
+is printed, along with DUPLICATE if the file is a duplicate of a
+file in the filesystem.
+
+The -nopad option informs mksquashfs to not pad the filesystem to a 4K multiple.
+This is performed by default to enable the output filesystem file to be mounted
+by loopback, which requires files to be a 4K multiple.  If the filesystem is
+being written to a block device, or is to be stored in a bootimage, the extra
+pad bytes are not needed.
+
+4. UNSQUASHFS
+-------------
+
+Unsquashfs allows you to decompress and extract a Squashfs filesystem without
+mounting it.  It can extract the entire filesystem, or a specific
+file or directory.
+
+Unsquashfs can decompress all official Squashfs filesystem versions.
+
+The Unsquashfs usage info is:
+
+SYNTAX: unsquashfs [options] filesystem [directories or files to extract]
+       -v[ersion]              print version, licence and copyright information
+       -d[est] <pathname>      unsquash to <pathname>, default "squashfs-root"
+       -q[uiet]                no verbose output
+       -n[o-progress]          don't display the progress bar
+       -no[-xattrs]            don't extract xattrs in file system
+       -x[attrs]               extract xattrs in file system (default)
+       -u[ser-xattrs]          only extract user xattrs in file system.
+                               Enables extracting xattrs
+       -p[rocessors] <number>  use <number> processors.  By default will use
+                               number of processors available
+       -i[nfo]                 print files as they are unsquashed
+       -li[nfo]                print files as they are unsquashed with file
+                               attributes (like ls -l output)
+       -l[s]                   list filesystem, but don't unsquash
+       -ll[s]                  list filesystem with file attributes (like
+                               ls -l output), but don't unsquash
+       -lln[umeric]            -lls but with numeric uids and gids
+       -lc                     list filesystem concisely, displaying only files
+                               and empty directories.  Don't unsquash
+       -llc                    list filesystem concisely with file attributes,
+                               displaying only files and empty directories.
+                               Don't unsquash
+       -o[ffset] <bytes>       skip <bytes> at start of <dest>
+                               Optionally a suffix of K, M or G can be given to specify
+                               Kbytes, Mbytes or Gbytes respectively.
+                               Default 0 bytes.
+       -f[orce]                if file already exists then overwrite
+       -ig[nore-errors]        Treat errors writing files to output as non-fatal
+       -st[rict-errors]        Treat all errors as fatal
+       -s[tat]                 display filesystem superblock information
+       -UTC                    Use UTC rather than local time zone when displaying time
+       -mkfs-time              display filesystem superblock time
+       -fstime                 synonym for -mkfs-time
+       -e[f] <extract file>    list of directories or files to extract.
+                               One per line
+       -da[ta-queue] <size>    Set data queue to <size> Mbytes.  Default 256
+                               Mbytes
+       -fr[ag-queue] <size>    Set fragment queue to <size> Mbytes.  Default
+                               256 Mbytes
+       -r[egex]                treat extract names as POSIX regular expressions
+                               rather than use the default shell wildcard
+                               expansion (globbing)
+
+Decompressors available:
+       gzip
+       lzo
+       lz4
+       xz
+       zstd
+
+To extract a subset of the filesystem, the filenames or directory
+trees that are to be extracted can be specified on the command line.  The
+files/directories should be specified using the full path to the
+files/directories as they appear within the Squashfs filesystem.  The
+files/directories will also be extracted to those positions within the specified
+destination directory.
+
+The extract files can also be given in a file using the "-e[f]" option.
+
+Similarly to Mksquashfs, wildcard matching is performed on the extract
+files.  Wildcard matching is enabled by default.
+
+Examples:
+
+  1. unsquashfs image.sqsh 'test/*.gz'
+
+     Extract all files matching "*.gz" in the top level directory "test".
+
+  2. unsquashfs image.sqsh '[Tt]est/example*'
+
+     Extract all files beginning with "example" inside top level directories
+     called "Test" or "test".
+
+  Using extended wildcards, negative matching is also possible.
+
+  3. unsquashfs image.sqsh 'test/!(*data*).gz'
+
+     Extract all files matching "*.gz" in top level directory "test",
+     except those with "data" in the name.
+
+
+4.1 Unsquashfs options
+----------------------
+
+The "-ls" option can be used to list the contents of a filesystem without
+decompressing the filesystem data itself.  The "-lls" option is similar
+but it also displays file attributes (ls -l style output).  The "-lln"
+option is the same but displays uids and gids numerically.
+
+The "-lc" option is similar to the -ls option except it only displays files
+and empty directories.  The -llc option displays file attributes.
+
+The "-info" option forces Unsquashfs to print each file as it is decompressed.
+The -"linfo" is similar but it also displays file attributes.
+
+The "-dest" option specifies the directory that is used to decompress
+the filesystem data.  If this option is not given then the filesystem is
+decompressed to the directory "squashfs-root" in the current working directory.
+
+The "-force" option forces Unsquashfs to output to the destination
+directory even if files or directories already exist.  This allows you
+to update an existing directory tree, or to Unsquashfs to a partially
+filled directory.  Without the "-force" option, Unsquashfs will
+refuse to overwrite any existing files, or to create any directories if they
+already exist.  This is done to protect data in case of mistakes, and
+so the "-force" option should be used with caution.
+
+The "-stat" option displays filesystem superblock information.  This is
+useful to discover the filesystem version, byte ordering, whether it has a NFS
+export table, and what options were used to compress the filesystem, etc.
+
+The -mkfs-time option displays the make filesystem time contained
+in the super-block.  This is displayed as the number of seconds since
+the epoch of 1970-01-01 00:00:00 UTC.
+
+The -UTC option makes Unsquashfs display all times in the UTC time zone
+rather than using the default local time zone.
+
+4.2. Dealing with errors
+------------------------
+
+Unsquashfs splits errors into two categories: fatal errors and non-fatal
+errors.
+
+Fatal errors are those which cause Unsquashfs to abort instantly.
+These are generally due to failure to read the filesystem (corruption),
+and/or failure to write files to the output filesystem, due to I/O error
+or out of space.  Generally anything which is unexpected is a fatal error.
+
+Non-fatal errors are generally where support is lacking in the
+output filesystem, and it can be considered to be an expected failure.
+This includes the inability to write extended attributes (xattrs) to
+a filesystem that doesn't support them, the inability to create files on
+filesystem that doesn't support them (i.e. symbolic links on VFAT), and the
+inability to execute privileged operations as a user-process.
+
+The user may well know the filesystem cannot support certain operations
+and would prefer Unsquashfs to ignore then without aborting.
+
+In the past Unsquashfs was much more tolerant of errors, in this
+release a significant number of errors that were non-fatal have been
+hardened to fatal.
+
+4.2.1. -ignore-errors
+
+This makes Unsquashfs behave like previous versions, and treats more
+errors as non-fatal.
+
+4.2.2 -strict-errors
+
+This makes Unsquashfs treat every error as fatal, and it will abort
+instantly.
+
+5. FILESYSTEM LAYOUT
+--------------------
+
+A squashfs filesystem consists of a maximum of nine parts, packed together on a
+byte alignment:
+
+        ---------------
+       |  superblock   |
+       |---------------|
+       |  compression  |
+       |    options    |
+       |---------------|
+       |  datablocks   |
+       |  & fragments  |
+       |---------------|
+       |  inode table  |
+       |---------------|
+       |   directory   |
+       |     table     |
+       |---------------|
+       |   fragment    |
+       |    table      |
+       |---------------|
+       |    export     |
+       |    table      |
+       |---------------|
+       |    uid/gid    |
+       |  lookup table |
+       |---------------|
+       |     xattr     |
+       |     table     |
+        ---------------
+
+Compressed data blocks are written to the filesystem as files are read from
+the source directory, and checked for duplicates.  Once all file data has been
+written the completed super-block, compression options, inode, directory,
+fragment, export, uid/gid lookup and xattr tables are written.
+
+5.1 Compression options
+-----------------------
+
+Compressors can optionally support compression specific options (e.g.
+dictionary size).  If non-default compression options have been used, then
+these are stored here.
+
+5.2 Inodes
+----------
+
+Metadata (inodes and directories) are compressed in 8Kbyte blocks.  Each
+compressed block is prefixed by a two byte length, the top bit is set if the
+block is uncompressed.  A block will be uncompressed if the -noI option is set,
+or if the compressed block was larger than the uncompressed block.
+
+Inodes are packed into the metadata blocks, and are not aligned to block
+boundaries, therefore inodes overlap compressed blocks.  Inodes are identified
+by a 48-bit number which encodes the location of the compressed metadata block
+containing the inode, and the byte offset into that block where the inode is
+placed (<block, offset>).
+
+To maximise compression there are different inodes for each file type
+(regular file, directory, device, etc.), the inode contents and length
+varying with the type.
+
+To further maximise compression, two types of regular file inode and
+directory inode are defined: inodes optimised for frequently occurring
+regular files and directories, and extended types where extra
+information has to be stored.
+
+5.3 Directories
+---------------
+
+Like inodes, directories are packed into compressed metadata blocks, stored
+in a directory table.  Directories are accessed using the start address of
+the metablock containing the directory and the offset into the
+decompressed block (<block, offset>).
+
+Directories are organised in a slightly complex way, and are not simply
+a list of file names.  The organisation takes advantage of the
+fact that (in most cases) the inodes of the files will be in the same
+compressed metadata block, and therefore, can share the start block.
+Directories are therefore organised in a two level list, a directory
+header containing the shared start block value, and a sequence of directory
+entries, each of which share the shared start block.  A new directory header
+is written once/if the inode start block changes.  The directory
+header/directory entry list is repeated as many times as necessary.
+
+Directories are sorted, and can contain a directory index to speed up
+file lookup.  Directory indexes store one entry per metablock, each entry
+storing the index/filename mapping to the first directory header
+in each metadata block.  Directories are sorted in alphabetical order,
+and at lookup the index is scanned linearly looking for the first filename
+alphabetically larger than the filename being looked up.  At this point the
+location of the metadata block the filename is in has been found.
+The general idea of the index is ensure only one metadata block needs to be
+decompressed to do a lookup irrespective of the length of the directory.
+This scheme has the advantage that it doesn't require extra memory overhead
+and doesn't require much extra storage on disk.
+
+5.4 File data
+-------------
+
+Regular files consist of a sequence of contiguous compressed blocks, and/or a
+compressed fragment block (tail-end packed block).   The compressed size
+of each datablock is stored in a block list contained within the
+file inode.
+
+To speed up access to datablocks when reading 'large' files (256 Mbytes or
+larger), the code implements an index cache that caches the mapping from
+block index to datablock location on disk.
+
+The index cache allows Squashfs to handle large files (up to 1.75 TiB) while
+retaining a simple and space-efficient block list on disk.  The cache
+is split into slots, caching up to eight 224 GiB files (128 KiB blocks).
+Larger files use multiple slots, with 1.75 TiB files using all 8 slots.
+The index cache is designed to be memory efficient, and by default uses
+16 KiB.
+
+5.5 Fragment lookup table
+-------------------------
+
+Regular files can contain a fragment index which is mapped to a fragment
+location on disk and compressed size using a fragment lookup table.  This
+fragment lookup table is itself stored compressed into metadata blocks.
+A second index table is used to locate these.  This second index table for
+speed of access (and because it is small) is read at mount time and cached
+in memory.
+
+5.6 Uid/gid lookup table
+------------------------
+
+For space efficiency regular files store uid and gid indexes, which are
+converted to 32-bit uids/gids using an id look up table.  This table is
+stored compressed into metadata blocks.  A second index table is used to
+locate these.  This second index table for speed of access (and because it
+is small) is read at mount time and cached in memory.
+
+5.7 Export table
+----------------
+
+To enable Squashfs filesystems to be exportable (via NFS etc.) filesystems
+can optionally (disabled with the -no-exports Mksquashfs option) contain
+an inode number to inode disk location lookup table.  This is required to
+enable Squashfs to map inode numbers passed in filehandles to the inode
+location on disk, which is necessary when the export code reinstantiates
+expired/flushed inodes.
+
+This table is stored compressed into metadata blocks.  A second index table is
+used to locate these.  This second index table for speed of access (and because
+it is small) is read at mount time and cached in memory.
+
+5.8 Xattr table
+---------------
+
+The xattr table contains extended attributes for each inode.  The xattrs
+for each inode are stored in a list, each list entry containing a type,
+name and value field.  The type field encodes the xattr prefix
+("user.", "trusted." etc) and it also encodes how the name/value fields
+should be interpreted.  Currently the type indicates whether the value
+is stored inline (in which case the value field contains the xattr value),
+or if it is stored out of line (in which case the value field stores a
+reference to where the actual value is stored).  This allows large values
+to be stored out of line improving scanning and lookup performance and it
+also allows values to be de-duplicated, the value being stored once, and
+all other occurences holding an out of line reference to that value.
+
+The xattr lists are packed into compressed 8K metadata blocks.
+To reduce overhead in inodes, rather than storing the on-disk
+location of the xattr list inside each inode, a 32-bit xattr id
+is stored.  This xattr id is mapped into the location of the xattr
+list using a second xattr id lookup table.
+
+6. AUTHOR INFO
+--------------
+
+Squashfs was written by Phillip Lougher, email phillip@squashfs.org.uk,
+in Chepstow, Wales, UK.   If you like the program, or have any problems,
+then please email me, as it's nice to get feedback!
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/README b/SQUASHFS/squashfs-tools-4.4/kernel/README
new file mode 100644 (file)
index 0000000..54ab958
--- /dev/null
@@ -0,0 +1,3 @@
+Squashfs is now in mainline at www.kernel.org.
+
+These files are obsolete and not updated.
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/Makefile b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/Makefile
new file mode 100644 (file)
index 0000000..0965287
--- /dev/null
@@ -0,0 +1,11 @@
+#
+# Makefile for the linux squashfs routines.
+#
+
+O_TARGET := squashfs.o
+
+obj-y  := inode.o squashfs2_0.o
+
+obj-m := $(O_TARGET)
+
+include $(TOPDIR)/Rules.make
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/inode.c b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/inode.c
new file mode 100644 (file)
index 0000000..54061f6
--- /dev/null
@@ -0,0 +1,2029 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * inode.c
+ */
+
+#include <linux/types.h>
+#include <linux/squashfs_fs.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/zlib.h>
+#include <linux/fs.h>
+#include <linux/smp_lock.h>
+#include <linux/locks.h>
+#include <linux/init.h>
+#include <linux/dcache.h>
+#include <linux/wait.h>
+#include <linux/blkdev.h>
+#include <linux/vmalloc.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+
+#include "squashfs.h"
+
+static struct super_block *squashfs_read_super(struct super_block *, void *, int);
+static void squashfs_put_super(struct super_block *);
+static int squashfs_statfs(struct super_block *, struct statfs *);
+static int squashfs_symlink_readpage(struct file *file, struct page *page);
+static int squashfs_readpage(struct file *file, struct page *page);
+static int squashfs_readpage4K(struct file *file, struct page *page);
+static int squashfs_readdir(struct file *, void *, filldir_t);
+static struct dentry *squashfs_lookup(struct inode *, struct dentry *);
+static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode);
+static long long read_blocklist(struct inode *inode, int index,
+                               int readahead_blks, char *block_list,
+                               unsigned short **block_p, unsigned int *bsize);
+
+static DECLARE_FSTYPE_DEV(squashfs_fs_type, "squashfs", squashfs_read_super);
+
+static unsigned char squashfs_filetype_table[] = {
+       DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
+};
+
+static struct super_operations squashfs_ops = {
+       .statfs = squashfs_statfs,
+       .put_super = squashfs_put_super,
+};
+
+SQSH_EXTERN struct address_space_operations squashfs_symlink_aops = {
+       .readpage = squashfs_symlink_readpage
+};
+
+SQSH_EXTERN struct address_space_operations squashfs_aops = {
+       .readpage = squashfs_readpage
+};
+
+SQSH_EXTERN struct address_space_operations squashfs_aops_4K = {
+       .readpage = squashfs_readpage4K
+};
+
+static struct file_operations squashfs_dir_ops = {
+       .read = generic_read_dir,
+       .readdir = squashfs_readdir
+};
+
+static struct inode_operations squashfs_dir_inode_ops = {
+       .lookup = squashfs_lookup
+};
+
+static struct buffer_head *get_block_length(struct super_block *s,
+                               int *cur_index, int *offset, int *c_byte)
+{
+       struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+       unsigned short temp;
+       struct buffer_head *bh;
+
+       if (!(bh = sb_bread(s, *cur_index)))
+               goto out;
+
+       if (msblk->devblksize - *offset == 1) {
+               if (msblk->swap)
+                       ((unsigned char *) &temp)[1] = *((unsigned char *)
+                               (bh->b_data + *offset));
+               else
+                       ((unsigned char *) &temp)[0] = *((unsigned char *)
+                               (bh->b_data + *offset));
+               brelse(bh);
+               if (!(bh = sb_bread(s, ++(*cur_index))))
+                       goto out;
+               if (msblk->swap)
+                       ((unsigned char *) &temp)[0] = *((unsigned char *)
+                               bh->b_data); 
+               else
+                       ((unsigned char *) &temp)[1] = *((unsigned char *)
+                               bh->b_data); 
+               *c_byte = temp;
+               *offset = 1;
+       } else {
+               if (msblk->swap) {
+                       ((unsigned char *) &temp)[1] = *((unsigned char *)
+                               (bh->b_data + *offset));
+                       ((unsigned char *) &temp)[0] = *((unsigned char *)
+                               (bh->b_data + *offset + 1)); 
+               } else {
+                       ((unsigned char *) &temp)[0] = *((unsigned char *)
+                               (bh->b_data + *offset));
+                       ((unsigned char *) &temp)[1] = *((unsigned char *)
+                               (bh->b_data + *offset + 1)); 
+               }
+               *c_byte = temp;
+               *offset += 2;
+       }
+
+       if (SQUASHFS_CHECK_DATA(msblk->sblk.flags)) {
+               if (*offset == msblk->devblksize) {
+                       brelse(bh);
+                       if (!(bh = sb_bread(s, ++(*cur_index))))
+                               goto out;
+                       *offset = 0;
+               }
+               if (*((unsigned char *) (bh->b_data + *offset)) !=
+                                               SQUASHFS_MARKER_BYTE) {
+                       ERROR("Metadata block marker corrupt @ %x\n",
+                                               *cur_index);
+                       brelse(bh);
+                       goto out;
+               }
+               (*offset)++;
+       }
+       return bh;
+
+out:
+       return NULL;
+}
+
+
+SQSH_EXTERN unsigned int squashfs_read_data(struct super_block *s, char *buffer,
+                       long long index, unsigned int length,
+                       long long *next_index)
+{
+       struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+       struct buffer_head *bh[((SQUASHFS_FILE_MAX_SIZE - 1) >>
+                       msblk->devblksize_log2) + 2];
+       unsigned int offset = index & ((1 << msblk->devblksize_log2) - 1);
+       unsigned int cur_index = index >> msblk->devblksize_log2;
+       int bytes, avail_bytes, b = 0, k;
+       char *c_buffer;
+       unsigned int compressed;
+       unsigned int c_byte = length;
+
+       if (c_byte) {
+               bytes = msblk->devblksize - offset;
+               compressed = SQUASHFS_COMPRESSED_BLOCK(c_byte);
+               c_buffer = compressed ? msblk->read_data : buffer;
+               c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte);
+
+               TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed
+                                       ? "" : "un", (unsigned int) c_byte);
+
+               if (!(bh[0] = sb_getblk(s, cur_index)))
+                       goto block_release;
+
+               for (b = 1; bytes < c_byte; b++) {
+                       if (!(bh[b] = sb_getblk(s, ++cur_index)))
+                               goto block_release;
+                       bytes += msblk->devblksize;
+               }
+               ll_rw_block(READ, b, bh);
+       } else {
+               if (!(bh[0] = get_block_length(s, &cur_index, &offset,
+                                                               &c_byte)))
+                       goto read_failure;
+
+               bytes = msblk->devblksize - offset;
+               compressed = SQUASHFS_COMPRESSED(c_byte);
+               c_buffer = compressed ? msblk->read_data : buffer;
+               c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
+
+               TRACE("Block @ 0x%llx, %scompressed size %d\n", index, compressed
+                                       ? "" : "un", (unsigned int) c_byte);
+
+               for (b = 1; bytes < c_byte; b++) {
+                       if (!(bh[b] = sb_getblk(s, ++cur_index)))
+                               goto block_release;
+                       bytes += msblk->devblksize;
+               }
+               ll_rw_block(READ, b - 1, bh + 1);
+       }
+
+       if (compressed)
+               down(&msblk->read_data_mutex);
+
+       for (bytes = 0, k = 0; k < b; k++) {
+               avail_bytes = (c_byte - bytes) > (msblk->devblksize - offset) ?
+                                       msblk->devblksize - offset :
+                                       c_byte - bytes;
+               wait_on_buffer(bh[k]);
+               if (!buffer_uptodate(bh[k]))
+                       goto block_release;
+               memcpy(c_buffer + bytes, bh[k]->b_data + offset, avail_bytes);
+               bytes += avail_bytes;
+               offset = 0;
+               brelse(bh[k]);
+       }
+
+       /*
+        * uncompress block
+        */
+       if (compressed) {
+               int zlib_err;
+
+               msblk->stream.next_in = c_buffer;
+               msblk->stream.avail_in = c_byte;
+               msblk->stream.next_out = buffer;
+               msblk->stream.avail_out = msblk->read_size;
+
+               if (((zlib_err = zlib_inflateInit(&msblk->stream)) != Z_OK) ||
+                               ((zlib_err = zlib_inflate(&msblk->stream, Z_FINISH))
+                                != Z_STREAM_END) || ((zlib_err =
+                               zlib_inflateEnd(&msblk->stream)) != Z_OK)) {
+                       ERROR("zlib_fs returned unexpected result 0x%x\n",
+                               zlib_err);
+                       bytes = 0;
+               } else
+                       bytes = msblk->stream.total_out;
+               
+               up(&msblk->read_data_mutex);
+       }
+
+       if (next_index)
+               *next_index = index + c_byte + (length ? 0 :
+                               (SQUASHFS_CHECK_DATA(msblk->sblk.flags)
+                                ? 3 : 2));
+       return bytes;
+
+block_release:
+       while (--b >= 0)
+               brelse(bh[b]);
+
+read_failure:
+       ERROR("sb_bread failed reading block 0x%x\n", cur_index);
+       return 0;
+}
+
+
+SQSH_EXTERN int squashfs_get_cached_block(struct super_block *s, char *buffer,
+                               long long block, unsigned int offset,
+                               int length, long long *next_block,
+                               unsigned int *next_offset)
+{
+       struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+       int n, i, bytes, return_length = length;
+       long long next_index;
+
+       TRACE("Entered squashfs_get_cached_block [%llx:%x]\n", block, offset);
+
+       while ( 1 ) {
+               for (i = 0; i < SQUASHFS_CACHED_BLKS; i++) 
+                       if (msblk->block_cache[i].block == block)
+                               break; 
+               
+               down(&msblk->block_cache_mutex);
+
+               if (i == SQUASHFS_CACHED_BLKS) {
+                       /* read inode header block */
+                       for (i = msblk->next_cache, n = SQUASHFS_CACHED_BLKS;
+                                       n ; n --, i = (i + 1) %
+                                       SQUASHFS_CACHED_BLKS)
+                               if (msblk->block_cache[i].block !=
+                                                       SQUASHFS_USED_BLK)
+                                       break;
+
+                       if (n == 0) {
+                               wait_queue_t wait;
+
+                               init_waitqueue_entry(&wait, current);
+                               add_wait_queue(&msblk->waitq, &wait);
+                               set_current_state(TASK_UNINTERRUPTIBLE);
+                               up(&msblk->block_cache_mutex);
+                               schedule();
+                               set_current_state(TASK_RUNNING);
+                               remove_wait_queue(&msblk->waitq, &wait);
+                               continue;
+                       }
+                       msblk->next_cache = (i + 1) % SQUASHFS_CACHED_BLKS;
+
+                       if (msblk->block_cache[i].block ==
+                                                       SQUASHFS_INVALID_BLK) {
+                               if (!(msblk->block_cache[i].data =
+                                               kmalloc(SQUASHFS_METADATA_SIZE,
+                                               GFP_KERNEL))) {
+                                       ERROR("Failed to allocate cache"
+                                                       "block\n");
+                                       up(&msblk->block_cache_mutex);
+                                       goto out;
+                               }
+                       }
+       
+                       msblk->block_cache[i].block = SQUASHFS_USED_BLK;
+                       up(&msblk->block_cache_mutex);
+
+                       if (!(msblk->block_cache[i].length =
+                                               squashfs_read_data(s,
+                                               msblk->block_cache[i].data,
+                                               block, 0, &next_index))) {
+                               ERROR("Unable to read cache block [%llx:%x]\n",
+                                               block, offset);
+                               goto out;
+                       }
+
+                       down(&msblk->block_cache_mutex);
+                       wake_up(&msblk->waitq);
+                       msblk->block_cache[i].block = block;
+                       msblk->block_cache[i].next_index = next_index;
+                       TRACE("Read cache block [%llx:%x]\n", block, offset);
+               }
+
+               if (msblk->block_cache[i].block != block) {
+                       up(&msblk->block_cache_mutex);
+                       continue;
+               }
+
+               if ((bytes = msblk->block_cache[i].length - offset) >= length) {
+                       if (buffer)
+                               memcpy(buffer, msblk->block_cache[i].data +
+                                               offset, length);
+                       if (msblk->block_cache[i].length - offset == length) {
+                               *next_block = msblk->block_cache[i].next_index;
+                               *next_offset = 0;
+                       } else {
+                               *next_block = block;
+                               *next_offset = offset + length;
+                       }
+                       up(&msblk->block_cache_mutex);
+                       goto finish;
+               } else {
+                       if (buffer) {
+                               memcpy(buffer, msblk->block_cache[i].data +
+                                               offset, bytes);
+                               buffer += bytes;
+                       }
+                       block = msblk->block_cache[i].next_index;
+                       up(&msblk->block_cache_mutex);
+                       length -= bytes;
+                       offset = 0;
+               }
+       }
+
+finish:
+       return return_length;
+out:
+       return 0;
+}
+
+
+static int get_fragment_location(struct super_block *s, unsigned int fragment,
+                               long long *fragment_start_block,
+                               unsigned int *fragment_size)
+{
+       struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+       long long start_block =
+               msblk->fragment_index[SQUASHFS_FRAGMENT_INDEX(fragment)];
+       int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment);
+       struct squashfs_fragment_entry fragment_entry;
+
+       if (msblk->swap) {
+               struct squashfs_fragment_entry sfragment_entry;
+
+               if (!squashfs_get_cached_block(s, (char *) &sfragment_entry,
+                                       start_block, offset,
+                                       sizeof(sfragment_entry), &start_block,
+                                       &offset))
+                       goto out;
+               SQUASHFS_SWAP_FRAGMENT_ENTRY(&fragment_entry, &sfragment_entry);
+       } else
+               if (!squashfs_get_cached_block(s, (char *) &fragment_entry,
+                                       start_block, offset,
+                                       sizeof(fragment_entry), &start_block,
+                                       &offset))
+                       goto out;
+
+       *fragment_start_block = fragment_entry.start_block;
+       *fragment_size = fragment_entry.size;
+
+       return 1;
+
+out:
+       return 0;
+}
+
+
+SQSH_EXTERN void release_cached_fragment(struct squashfs_sb_info *msblk, struct
+                                       squashfs_fragment_cache *fragment)
+{
+       down(&msblk->fragment_mutex);
+       fragment->locked --;
+       wake_up(&msblk->fragment_wait_queue);
+       up(&msblk->fragment_mutex);
+}
+
+
+SQSH_EXTERN struct squashfs_fragment_cache *get_cached_fragment(struct super_block
+                                       *s, long long start_block,
+                                       int length)
+{
+       int i, n;
+       struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+
+       while ( 1 ) {
+               down(&msblk->fragment_mutex);
+
+               for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS &&
+                               msblk->fragment[i].block != start_block; i++);
+
+               if (i == SQUASHFS_CACHED_FRAGMENTS) {
+                       for (i = msblk->next_fragment, n =
+                               SQUASHFS_CACHED_FRAGMENTS; n &&
+                               msblk->fragment[i].locked; n--, i = (i + 1) %
+                               SQUASHFS_CACHED_FRAGMENTS);
+
+                       if (n == 0) {
+                               wait_queue_t wait;
+
+                               init_waitqueue_entry(&wait, current);
+                               add_wait_queue(&msblk->fragment_wait_queue,
+                                                                       &wait);
+                               set_current_state(TASK_UNINTERRUPTIBLE);
+                               up(&msblk->fragment_mutex);
+                               schedule();
+                               set_current_state(TASK_RUNNING);
+                               remove_wait_queue(&msblk->fragment_wait_queue,
+                                                                       &wait);
+                               continue;
+                       }
+                       msblk->next_fragment = (msblk->next_fragment + 1) %
+                               SQUASHFS_CACHED_FRAGMENTS;
+                       
+                       if (msblk->fragment[i].data == NULL)
+                               if (!(msblk->fragment[i].data = SQUASHFS_ALLOC
+                                               (SQUASHFS_FILE_MAX_SIZE))) {
+                                       ERROR("Failed to allocate fragment "
+                                                       "cache block\n");
+                                       up(&msblk->fragment_mutex);
+                                       goto out;
+                               }
+
+                       msblk->fragment[i].block = SQUASHFS_INVALID_BLK;
+                       msblk->fragment[i].locked = 1;
+                       up(&msblk->fragment_mutex);
+
+                       if (!(msblk->fragment[i].length = squashfs_read_data(s,
+                                               msblk->fragment[i].data,
+                                               start_block, length, NULL))) {
+                               ERROR("Unable to read fragment cache block "
+                                                       "[%llx]\n", start_block);
+                               msblk->fragment[i].locked = 0;
+                               goto out;
+                       }
+
+                       msblk->fragment[i].block = start_block;
+                       TRACE("New fragment %d, start block %lld, locked %d\n",
+                                               i, msblk->fragment[i].block,
+                                               msblk->fragment[i].locked);
+                       break;
+               }
+
+               msblk->fragment[i].locked++;
+               up(&msblk->fragment_mutex);
+               TRACE("Got fragment %d, start block %lld, locked %d\n", i,
+                                               msblk->fragment[i].block,
+                                               msblk->fragment[i].locked);
+               break;
+       }
+
+       return &msblk->fragment[i];
+
+out:
+       return NULL;
+}
+
+
+static struct inode *squashfs_new_inode(struct super_block *s,
+               struct squashfs_base_inode_header *inodeb)
+{
+       struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+       struct inode *i = new_inode(s);
+
+       if (i) {
+               i->i_ino = inodeb->inode_number;
+               i->i_mtime = inodeb->mtime;
+               i->i_atime = inodeb->mtime;
+               i->i_ctime = inodeb->mtime;
+               i->i_uid = msblk->uid[inodeb->uid];
+               i->i_mode = inodeb->mode;
+               i->i_size = 0;
+               if (inodeb->guid == SQUASHFS_GUIDS)
+                       i->i_gid = i->i_uid;
+               else
+                       i->i_gid = msblk->guid[inodeb->guid];
+       }
+
+       return i;
+}
+
+
+static struct inode *squashfs_iget(struct super_block *s, squashfs_inode_t inode)
+{
+       struct inode *i;
+       struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+       struct squashfs_super_block *sblk = &msblk->sblk;
+       long long block = SQUASHFS_INODE_BLK(inode) +
+               sblk->inode_table_start;
+       unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
+       long long next_block;
+       unsigned int next_offset;
+       union squashfs_inode_header id, sid;
+       struct squashfs_base_inode_header *inodeb = &id.base,
+                                         *sinodeb = &sid.base;
+
+       TRACE("Entered squashfs_iget\n");
+
+       if (msblk->swap) {
+               if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
+                                       offset, sizeof(*sinodeb), &next_block,
+                                       &next_offset))
+                       goto failed_read;
+               SQUASHFS_SWAP_BASE_INODE_HEADER(inodeb, sinodeb,
+                                       sizeof(*sinodeb));
+       } else
+               if (!squashfs_get_cached_block(s, (char *) inodeb, block,
+                                       offset, sizeof(*inodeb), &next_block,
+                                       &next_offset))
+                       goto failed_read;
+
+       switch(inodeb->inode_type) {
+               case SQUASHFS_FILE_TYPE: {
+                       unsigned int frag_size;
+                       long long frag_blk;
+                       struct squashfs_reg_inode_header *inodep = &id.reg;
+                       struct squashfs_reg_inode_header *sinodep = &sid.reg;
+                               
+                       if (msblk->swap) {
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               sinodep, block, offset,
+                                               sizeof(*sinodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+                               SQUASHFS_SWAP_REG_INODE_HEADER(inodep, sinodep);
+                       } else
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               inodep, block, offset,
+                                               sizeof(*inodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+
+                       frag_blk = SQUASHFS_INVALID_BLK;
+                       if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
+                                       !get_fragment_location(s,
+                                       inodep->fragment, &frag_blk, &frag_size))
+                               goto failed_read;
+                               
+                       if((i = squashfs_new_inode(s, inodeb)) == NULL)
+                               goto failed_read1;
+
+                       i->i_nlink = 1;
+                       i->i_size = inodep->file_size;
+                       i->i_fop = &generic_ro_fops;
+                       i->i_mode |= S_IFREG;
+                       i->i_blocks = ((i->i_size - 1) >> 9) + 1;
+                       i->i_blksize = PAGE_CACHE_SIZE;
+                       SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
+                       SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
+                       SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
+                       SQUASHFS_I(i)->start_block = inodep->start_block;
+                       SQUASHFS_I(i)->u.s1.block_list_start = next_block;
+                       SQUASHFS_I(i)->offset = next_offset;
+                       if (sblk->block_size > 4096)
+                               i->i_data.a_ops = &squashfs_aops;
+                       else
+                               i->i_data.a_ops = &squashfs_aops_4K;
+
+                       TRACE("File inode %x:%x, start_block %llx, "
+                                       "block_list_start %llx, offset %x\n",
+                                       SQUASHFS_INODE_BLK(inode), offset,
+                                       inodep->start_block, next_block,
+                                       next_offset);
+                       break;
+               }
+               case SQUASHFS_LREG_TYPE: {
+                       unsigned int frag_size;
+                       long long frag_blk;
+                       struct squashfs_lreg_inode_header *inodep = &id.lreg;
+                       struct squashfs_lreg_inode_header *sinodep = &sid.lreg;
+                               
+                       if (msblk->swap) {
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               sinodep, block, offset,
+                                               sizeof(*sinodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+                               SQUASHFS_SWAP_LREG_INODE_HEADER(inodep, sinodep);
+                       } else
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               inodep, block, offset,
+                                               sizeof(*inodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+
+                       frag_blk = SQUASHFS_INVALID_BLK;
+                       if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
+                                       !get_fragment_location(s,
+                                       inodep->fragment, &frag_blk, &frag_size))
+                               goto failed_read;
+                               
+                       if((i = squashfs_new_inode(s, inodeb)) == NULL)
+                               goto failed_read1;
+
+                       i->i_nlink = inodep->nlink;
+                       i->i_size = inodep->file_size;
+                       i->i_fop = &generic_ro_fops;
+                       i->i_mode |= S_IFREG;
+                       i->i_blocks = ((i->i_size - 1) >> 9) + 1;
+                       i->i_blksize = PAGE_CACHE_SIZE;
+                       SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
+                       SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
+                       SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
+                       SQUASHFS_I(i)->start_block = inodep->start_block;
+                       SQUASHFS_I(i)->u.s1.block_list_start = next_block;
+                       SQUASHFS_I(i)->offset = next_offset;
+                       if (sblk->block_size > 4096)
+                               i->i_data.a_ops = &squashfs_aops;
+                       else
+                               i->i_data.a_ops = &squashfs_aops_4K;
+
+                       TRACE("File inode %x:%x, start_block %llx, "
+                                       "block_list_start %llx, offset %x\n",
+                                       SQUASHFS_INODE_BLK(inode), offset,
+                                       inodep->start_block, next_block,
+                                       next_offset);
+                       break;
+               }
+               case SQUASHFS_DIR_TYPE: {
+                       struct squashfs_dir_inode_header *inodep = &id.dir;
+                       struct squashfs_dir_inode_header *sinodep = &sid.dir;
+
+                       if (msblk->swap) {
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               sinodep, block, offset,
+                                               sizeof(*sinodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+                               SQUASHFS_SWAP_DIR_INODE_HEADER(inodep, sinodep);
+                       } else
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               inodep, block, offset,
+                                               sizeof(*inodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+
+                       if((i = squashfs_new_inode(s, inodeb)) == NULL)
+                               goto failed_read1;
+
+                       i->i_nlink = inodep->nlink;
+                       i->i_size = inodep->file_size;
+                       i->i_op = &squashfs_dir_inode_ops;
+                       i->i_fop = &squashfs_dir_ops;
+                       i->i_mode |= S_IFDIR;
+                       SQUASHFS_I(i)->start_block = inodep->start_block;
+                       SQUASHFS_I(i)->offset = inodep->offset;
+                       SQUASHFS_I(i)->u.s2.directory_index_count = 0;
+                       SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode;
+
+                       TRACE("Directory inode %x:%x, start_block %x, offset "
+                                       "%x\n", SQUASHFS_INODE_BLK(inode),
+                                       offset, inodep->start_block,
+                                       inodep->offset);
+                       break;
+               }
+               case SQUASHFS_LDIR_TYPE: {
+                       struct squashfs_ldir_inode_header *inodep = &id.ldir;
+                       struct squashfs_ldir_inode_header *sinodep = &sid.ldir;
+
+                       if (msblk->swap) {
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               sinodep, block, offset,
+                                               sizeof(*sinodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+                               SQUASHFS_SWAP_LDIR_INODE_HEADER(inodep,
+                                               sinodep);
+                       } else
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               inodep, block, offset,
+                                               sizeof(*inodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+
+                       if((i = squashfs_new_inode(s, inodeb)) == NULL)
+                               goto failed_read1;
+
+                       i->i_nlink = inodep->nlink;
+                       i->i_size = inodep->file_size;
+                       i->i_op = &squashfs_dir_inode_ops;
+                       i->i_fop = &squashfs_dir_ops;
+                       i->i_mode |= S_IFDIR;
+                       SQUASHFS_I(i)->start_block = inodep->start_block;
+                       SQUASHFS_I(i)->offset = inodep->offset;
+                       SQUASHFS_I(i)->u.s2.directory_index_start = next_block;
+                       SQUASHFS_I(i)->u.s2.directory_index_offset =
+                                                               next_offset;
+                       SQUASHFS_I(i)->u.s2.directory_index_count =
+                                                               inodep->i_count;
+                       SQUASHFS_I(i)->u.s2.parent_inode = inodep->parent_inode;
+
+                       TRACE("Long directory inode %x:%x, start_block %x, "
+                                       "offset %x\n",
+                                       SQUASHFS_INODE_BLK(inode), offset,
+                                       inodep->start_block, inodep->offset);
+                       break;
+               }
+               case SQUASHFS_SYMLINK_TYPE: {
+                       struct squashfs_symlink_inode_header *inodep =
+                                                               &id.symlink;
+                       struct squashfs_symlink_inode_header *sinodep =
+                                                               &sid.symlink;
+       
+                       if (msblk->swap) {
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               sinodep, block, offset,
+                                               sizeof(*sinodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+                               SQUASHFS_SWAP_SYMLINK_INODE_HEADER(inodep,
+                                                               sinodep);
+                       } else
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               inodep, block, offset,
+                                               sizeof(*inodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+
+                       if((i = squashfs_new_inode(s, inodeb)) == NULL)
+                               goto failed_read1;
+
+                       i->i_nlink = inodep->nlink;
+                       i->i_size = inodep->symlink_size;
+                       i->i_op = &page_symlink_inode_operations;
+                       i->i_data.a_ops = &squashfs_symlink_aops;
+                       i->i_mode |= S_IFLNK;
+                       SQUASHFS_I(i)->start_block = next_block;
+                       SQUASHFS_I(i)->offset = next_offset;
+
+                       TRACE("Symbolic link inode %x:%x, start_block %llx, "
+                                       "offset %x\n",
+                                       SQUASHFS_INODE_BLK(inode), offset,
+                                       next_block, next_offset);
+                       break;
+                }
+                case SQUASHFS_BLKDEV_TYPE:
+                case SQUASHFS_CHRDEV_TYPE: {
+                       struct squashfs_dev_inode_header *inodep = &id.dev;
+                       struct squashfs_dev_inode_header *sinodep = &sid.dev;
+
+                       if (msblk->swap) {
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               sinodep, block, offset,
+                                               sizeof(*sinodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+                               SQUASHFS_SWAP_DEV_INODE_HEADER(inodep, sinodep);
+                       } else  
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               inodep, block, offset,
+                                               sizeof(*inodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+
+                       if ((i = squashfs_new_inode(s, inodeb)) == NULL)
+                               goto failed_read1;
+
+                       i->i_nlink = inodep->nlink;
+                       i->i_mode |= (inodeb->inode_type ==
+                                       SQUASHFS_CHRDEV_TYPE) ?  S_IFCHR :
+                                       S_IFBLK;
+                       init_special_inode(i, i->i_mode, inodep->rdev);
+
+                       TRACE("Device inode %x:%x, rdev %x\n",
+                                       SQUASHFS_INODE_BLK(inode), offset,
+                                       inodep->rdev);
+                       break;
+                }
+                case SQUASHFS_FIFO_TYPE:
+                case SQUASHFS_SOCKET_TYPE: {
+                       struct squashfs_ipc_inode_header *inodep = &id.ipc;
+                       struct squashfs_ipc_inode_header *sinodep = &sid.ipc;
+
+                       if (msblk->swap) {
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               sinodep, block, offset,
+                                               sizeof(*sinodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+                               SQUASHFS_SWAP_IPC_INODE_HEADER(inodep, sinodep);
+                       } else  
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               inodep, block, offset,
+                                               sizeof(*inodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+
+                       if ((i = squashfs_new_inode(s, inodeb)) == NULL)
+                               goto failed_read1;
+
+                       i->i_nlink = inodep->nlink;
+                       i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
+                                                       ? S_IFIFO : S_IFSOCK;
+                       init_special_inode(i, i->i_mode, 0);
+                       break;
+                }
+                default:
+                       ERROR("Unknown inode type %d in squashfs_iget!\n",
+                                       inodeb->inode_type);
+                       goto failed_read1;
+       }
+       
+       insert_inode_hash(i);
+       return i;
+
+failed_read:
+       ERROR("Unable to read inode [%llx:%x]\n", block, offset);
+
+failed_read1:
+       return NULL;
+}
+
+
+int read_fragment_index_table(struct super_block *s)
+{
+       struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+       struct squashfs_super_block *sblk = &msblk->sblk;
+
+       if (!(msblk->fragment_index = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES
+                                       (sblk->fragments), GFP_KERNEL))) {
+               ERROR("Failed to allocate uid/gid table\n");
+               return 0;
+       }
+   
+       if (SQUASHFS_FRAGMENT_INDEX_BYTES(sblk->fragments) &&
+                                       !squashfs_read_data(s, (char *)
+                                       msblk->fragment_index,
+                                       sblk->fragment_table_start,
+                                       SQUASHFS_FRAGMENT_INDEX_BYTES
+                                       (sblk->fragments) |
+                                       SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
+               ERROR("unable to read fragment index table\n");
+               return 0;
+       }
+
+       if (msblk->swap) {
+               int i;
+               long long fragment;
+
+               for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES(sblk->fragments);
+                                                                       i++) {
+                       SQUASHFS_SWAP_FRAGMENT_INDEXES((&fragment),
+                                               &msblk->fragment_index[i], 1);
+                       msblk->fragment_index[i] = fragment;
+               }
+       }
+
+       return 1;
+}
+
+
+static int supported_squashfs_filesystem(struct squashfs_sb_info *msblk, int silent)
+{
+       struct squashfs_super_block *sblk = &msblk->sblk;
+
+       msblk->iget = squashfs_iget;
+       msblk->read_blocklist = read_blocklist;
+       msblk->read_fragment_index_table = read_fragment_index_table;
+
+       if (sblk->s_major == 1) {
+               if (!squashfs_1_0_supported(msblk)) {
+                       SERROR("Major/Minor mismatch, Squashfs 1.0 filesystems "
+                               "are unsupported\n");
+                       SERROR("Please recompile with "
+                               "Squashfs 1.0 support enabled\n");
+                       return 0;
+               }
+       } else if (sblk->s_major == 2) {
+               if (!squashfs_2_0_supported(msblk)) {
+                       SERROR("Major/Minor mismatch, Squashfs 2.0 filesystems "
+                               "are unsupported\n");
+                       SERROR("Please recompile with "
+                               "Squashfs 2.0 support enabled\n");
+                       return 0;
+               }
+       } else if(sblk->s_major != SQUASHFS_MAJOR || sblk->s_minor >
+                       SQUASHFS_MINOR) {
+               SERROR("Major/Minor mismatch, trying to mount newer %d.%d "
+                               "filesystem\n", sblk->s_major, sblk->s_minor);
+               SERROR("Please update your kernel\n");
+               return 0;
+       }
+
+       return 1;
+}
+
+
+static struct super_block *squashfs_read_super(struct super_block *s,
+               void *data, int silent)
+{
+       kdev_t dev = s->s_dev;
+       struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+       struct squashfs_super_block *sblk = &msblk->sblk;
+       int i;
+       struct inode *root;
+
+       if (!(msblk->stream.workspace = vmalloc(zlib_inflate_workspacesize()))) {
+               ERROR("Failed to allocate zlib workspace\n");
+               goto failed_mount;
+       }
+
+       msblk->devblksize = get_hardsect_size(dev);
+       if(msblk->devblksize < BLOCK_SIZE)
+               msblk->devblksize = BLOCK_SIZE;
+       msblk->devblksize_log2 = ffz(~msblk->devblksize);
+        set_blocksize(dev, msblk->devblksize);
+       s->s_blocksize = msblk->devblksize;
+       s->s_blocksize_bits = msblk->devblksize_log2;
+       
+       init_MUTEX(&msblk->read_data_mutex);
+       init_MUTEX(&msblk->read_page_mutex);
+       init_MUTEX(&msblk->block_cache_mutex);
+       init_MUTEX(&msblk->fragment_mutex);
+       
+       init_waitqueue_head(&msblk->waitq);
+       init_waitqueue_head(&msblk->fragment_wait_queue);
+
+       if (!squashfs_read_data(s, (char *) sblk, SQUASHFS_START,
+                                       sizeof(struct squashfs_super_block) |
+                                       SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
+               SERROR("unable to read superblock\n");
+               goto failed_mount;
+       }
+
+       /* Check it is a SQUASHFS superblock */
+       msblk->swap = 0;
+       if ((s->s_magic = sblk->s_magic) != SQUASHFS_MAGIC) {
+               if (sblk->s_magic == SQUASHFS_MAGIC_SWAP) {
+                       struct squashfs_super_block ssblk;
+
+                       WARNING("Mounting a different endian SQUASHFS "
+                               "filesystem on %s\n", bdevname(dev));
+
+                       SQUASHFS_SWAP_SUPER_BLOCK(&ssblk, sblk);
+                       memcpy(sblk, &ssblk, sizeof(struct squashfs_super_block));
+                       msblk->swap = 1;
+               } else  {
+                       SERROR("Can't find a SQUASHFS superblock on %s\n",
+                                                       bdevname(dev));
+                       goto failed_mount;
+               }
+       }
+
+       /* Check the MAJOR & MINOR versions */
+       if(!supported_squashfs_filesystem(msblk, silent))
+               goto failed_mount;
+
+       TRACE("Found valid superblock on %s\n", bdevname(dev));
+       TRACE("Inodes are %scompressed\n",
+                                       SQUASHFS_UNCOMPRESSED_INODES
+                                       (sblk->flags) ? "un" : "");
+       TRACE("Data is %scompressed\n",
+                                       SQUASHFS_UNCOMPRESSED_DATA(sblk->flags)
+                                       ? "un" : "");
+       TRACE("Check data is %s present in the filesystem\n",
+                                       SQUASHFS_CHECK_DATA(sblk->flags) ?
+                                       "" : "not");
+       TRACE("Filesystem size %lld bytes\n", sblk->bytes_used);
+       TRACE("Block size %d\n", sblk->block_size);
+       TRACE("Number of inodes %d\n", sblk->inodes);
+       if (sblk->s_major > 1)
+               TRACE("Number of fragments %d\n", sblk->fragments);
+       TRACE("Number of uids %d\n", sblk->no_uids);
+       TRACE("Number of gids %d\n", sblk->no_guids);
+       TRACE("sblk->inode_table_start %llx\n", sblk->inode_table_start);
+       TRACE("sblk->directory_table_start %llx\n", sblk->directory_table_start);
+       if (sblk->s_major > 1)
+               TRACE("sblk->fragment_table_start %llx\n",
+                                       sblk->fragment_table_start);
+       TRACE("sblk->uid_start %llx\n", sblk->uid_start);
+
+       s->s_flags |= MS_RDONLY;
+       s->s_op = &squashfs_ops;
+
+       /* Init inode_table block pointer array */
+       if (!(msblk->block_cache = kmalloc(sizeof(struct squashfs_cache) *
+                                       SQUASHFS_CACHED_BLKS, GFP_KERNEL))) {
+               ERROR("Failed to allocate block cache\n");
+               goto failed_mount;
+       }
+
+       for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
+               msblk->block_cache[i].block = SQUASHFS_INVALID_BLK;
+
+       msblk->next_cache = 0;
+
+       /* Allocate read_data block */
+       msblk->read_size = (sblk->block_size < SQUASHFS_METADATA_SIZE) ?
+                                       SQUASHFS_METADATA_SIZE :
+                                       sblk->block_size;
+
+       if (!(msblk->read_data = kmalloc(msblk->read_size, GFP_KERNEL))) {
+               ERROR("Failed to allocate read_data block\n");
+               goto failed_mount;
+       }
+
+       /* Allocate read_page block */
+       if (!(msblk->read_page = kmalloc(sblk->block_size, GFP_KERNEL))) {
+               ERROR("Failed to allocate read_page block\n");
+               goto failed_mount;
+       }
+
+       /* Allocate uid and gid tables */
+       if (!(msblk->uid = kmalloc((sblk->no_uids + sblk->no_guids) *
+                                       sizeof(unsigned int), GFP_KERNEL))) {
+               ERROR("Failed to allocate uid/gid table\n");
+               goto failed_mount;
+       }
+       msblk->guid = msblk->uid + sblk->no_uids;
+   
+       if (msblk->swap) {
+               unsigned int suid[sblk->no_uids + sblk->no_guids];
+
+               if (!squashfs_read_data(s, (char *) &suid, sblk->uid_start,
+                                       ((sblk->no_uids + sblk->no_guids) *
+                                        sizeof(unsigned int)) |
+                                       SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
+                       ERROR("unable to read uid/gid table\n");
+                       goto failed_mount;
+               }
+
+               SQUASHFS_SWAP_DATA(msblk->uid, suid, (sblk->no_uids +
+                       sblk->no_guids), (sizeof(unsigned int) * 8));
+       } else
+               if (!squashfs_read_data(s, (char *) msblk->uid, sblk->uid_start,
+                                       ((sblk->no_uids + sblk->no_guids) *
+                                        sizeof(unsigned int)) |
+                                       SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
+                       ERROR("unable to read uid/gid table\n");
+                       goto failed_mount;
+               }
+
+
+       if (sblk->s_major == 1 && squashfs_1_0_supported(msblk))
+               goto allocate_root;
+
+       if (!(msblk->fragment = kmalloc(sizeof(struct squashfs_fragment_cache) *
+                               SQUASHFS_CACHED_FRAGMENTS, GFP_KERNEL))) {
+               ERROR("Failed to allocate fragment block cache\n");
+               goto failed_mount;
+       }
+
+       for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) {
+               msblk->fragment[i].locked = 0;
+               msblk->fragment[i].block = SQUASHFS_INVALID_BLK;
+               msblk->fragment[i].data = NULL;
+       }
+
+       msblk->next_fragment = 0;
+
+       /* Allocate fragment index table */
+       if(msblk->read_fragment_index_table(s) == 0)
+               goto failed_mount;
+
+allocate_root:
+       if ((root = (msblk->iget)(s, sblk->root_inode)) == NULL)
+               goto failed_mount;
+
+       if ((s->s_root = d_alloc_root(root)) == NULL) {
+               ERROR("Root inode create failed\n");
+               iput(root);
+               goto failed_mount;
+       }
+
+       TRACE("Leaving squashfs_read_super\n");
+       return s;
+
+failed_mount:
+       kfree(msblk->fragment_index);
+       kfree(msblk->fragment);
+       kfree(msblk->uid);
+       kfree(msblk->read_page);
+       kfree(msblk->read_data);
+       kfree(msblk->block_cache);
+       kfree(msblk->fragment_index_2);
+       vfree(msblk->stream.workspace);
+       return NULL;
+}
+
+
+static int squashfs_statfs(struct super_block *s, struct statfs *buf)
+{
+       struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+       struct squashfs_super_block *sblk = &msblk->sblk;
+
+       TRACE("Entered squashfs_statfs\n");
+
+       buf->f_type = SQUASHFS_MAGIC;
+       buf->f_bsize = sblk->block_size;
+       buf->f_blocks = ((sblk->bytes_used - 1) >> sblk->block_log) + 1;
+       buf->f_bfree = buf->f_bavail = 0;
+       buf->f_files = sblk->inodes;
+       buf->f_ffree = 0;
+       buf->f_namelen = SQUASHFS_NAME_LEN;
+
+       return 0;
+}
+
+
+static int squashfs_symlink_readpage(struct file *file, struct page *page)
+{
+       struct inode *inode = page->mapping->host;
+       int index = page->index << PAGE_CACHE_SHIFT, length, bytes;
+       long long block = SQUASHFS_I(inode)->start_block;
+       int offset = SQUASHFS_I(inode)->offset;
+       void *pageaddr = kmap(page);
+
+       TRACE("Entered squashfs_symlink_readpage, page index %ld, start block "
+                               "%llx, offset %x\n", page->index,
+                               SQUASHFS_I(inode)->start_block,
+                               SQUASHFS_I(inode)->offset);
+
+       for (length = 0; length < index; length += bytes) {
+               if (!(bytes = squashfs_get_cached_block(inode->i_sb, NULL,
+                               block, offset, PAGE_CACHE_SIZE, &block,
+                               &offset))) {
+                       ERROR("Unable to read symbolic link [%llx:%x]\n", block,
+                                       offset);
+                       goto skip_read;
+               }
+       }
+
+       if (length != index) {
+               ERROR("(squashfs_symlink_readpage) length != index\n");
+               bytes = 0;
+               goto skip_read;
+       }
+
+       bytes = (i_size_read(inode) - length) > PAGE_CACHE_SIZE ? PAGE_CACHE_SIZE :
+                                       i_size_read(inode) - length;
+
+       if (!(bytes = squashfs_get_cached_block(inode->i_sb, pageaddr, block,
+                                       offset, bytes, &block, &offset)))
+               ERROR("Unable to read symbolic link [%llx:%x]\n", block, offset);
+
+skip_read:
+       memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
+       kunmap(page);
+       SetPageUptodate(page);
+       UnlockPage(page);
+
+       return 0;
+}
+
+
+struct meta_index *locate_meta_index(struct inode *inode, int index, int offset)
+{
+       struct meta_index *meta = NULL;
+       struct squashfs_sb_info *msblk = &inode->i_sb->u.squashfs_sb;
+       int i;
+
+       down(&msblk->meta_index_mutex);
+
+       TRACE("locate_meta_index: index %d, offset %d\n", index, offset);
+
+       if(msblk->meta_index == NULL)
+               goto not_allocated;
+
+       for (i = 0; i < SQUASHFS_META_NUMBER; i ++)
+               if (msblk->meta_index[i].inode_number == inode->i_ino &&
+                               msblk->meta_index[i].offset >= offset &&
+                               msblk->meta_index[i].offset <= index &&
+                               msblk->meta_index[i].locked == 0) {
+                       TRACE("locate_meta_index: entry %d, offset %d\n", i,
+                                       msblk->meta_index[i].offset);
+                       meta = &msblk->meta_index[i];
+                       offset = meta->offset;
+               }
+
+       if (meta)
+               meta->locked = 1;
+
+not_allocated:
+       up(&msblk->meta_index_mutex);
+
+       return meta;
+}
+
+
+struct meta_index *empty_meta_index(struct inode *inode, int offset, int skip)
+{
+       struct squashfs_sb_info *msblk = &inode->i_sb->u.squashfs_sb;
+       struct meta_index *meta = NULL;
+       int i;
+
+       down(&msblk->meta_index_mutex);
+
+       TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip);
+
+       if(msblk->meta_index == NULL) {
+               if (!(msblk->meta_index = kmalloc(sizeof(struct meta_index) *
+                                       SQUASHFS_META_NUMBER, GFP_KERNEL))) {
+                       ERROR("Failed to allocate meta_index\n");
+                       goto failed;
+               }
+               for(i = 0; i < SQUASHFS_META_NUMBER; i++) {
+                       msblk->meta_index[i].inode_number = 0;
+                       msblk->meta_index[i].locked = 0;
+               }
+               msblk->next_meta_index = 0;
+       }
+
+       for(i = SQUASHFS_META_NUMBER; i &&
+                       msblk->meta_index[msblk->next_meta_index].locked; i --)
+               msblk->next_meta_index = (msblk->next_meta_index + 1) %
+                       SQUASHFS_META_NUMBER;
+
+       if(i == 0) {
+               TRACE("empty_meta_index: failed!\n");
+               goto failed;
+       }
+
+       TRACE("empty_meta_index: returned meta entry %d, %p\n",
+                       msblk->next_meta_index,
+                       &msblk->meta_index[msblk->next_meta_index]);
+
+       meta = &msblk->meta_index[msblk->next_meta_index];
+       msblk->next_meta_index = (msblk->next_meta_index + 1) %
+                       SQUASHFS_META_NUMBER;
+
+       meta->inode_number = inode->i_ino;
+       meta->offset = offset;
+       meta->skip = skip;
+       meta->entries = 0;
+       meta->locked = 1;
+
+failed:
+       up(&msblk->meta_index_mutex);
+       return meta;
+}
+
+
+void release_meta_index(struct inode *inode, struct meta_index *meta)
+{
+       meta->locked = 0;
+}
+
+
+static int read_block_index(struct super_block *s, int blocks, char *block_list,
+               long long *start_block, int *offset)
+{
+       struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+       unsigned int *block_listp;
+       int block = 0;
+       
+       if (msblk->swap) {
+               char sblock_list[blocks << 2];
+
+               if (!squashfs_get_cached_block(s, sblock_list, *start_block,
+                               *offset, blocks << 2, start_block, offset)) {
+                       ERROR("Unable to read block list [%llx:%x]\n",
+                               *start_block, *offset);
+                       goto failure;
+               }
+               SQUASHFS_SWAP_INTS(((unsigned int *)block_list),
+                               ((unsigned int *)sblock_list), blocks);
+       } else
+               if (!squashfs_get_cached_block(s, block_list, *start_block,
+                               *offset, blocks << 2, start_block, offset)) {
+                       ERROR("Unable to read block list [%llx:%x]\n",
+                               *start_block, *offset);
+                       goto failure;
+               }
+
+       for (block_listp = (unsigned int *) block_list; blocks;
+                               block_listp++, blocks --)
+               block += SQUASHFS_COMPRESSED_SIZE_BLOCK(*block_listp);
+
+       return block;
+
+failure:
+       return -1;
+}
+
+
+#define SIZE 256
+
+static inline int calculate_skip(int blocks) {
+       int skip = (blocks - 1) / ((SQUASHFS_SLOTS * SQUASHFS_META_ENTRIES + 1) * SQUASHFS_META_INDEXES);
+       return skip >= 7 ? 7 : skip + 1;
+}
+
+
+static int get_meta_index(struct inode *inode, int index,
+               long long *index_block, int *index_offset,
+               long long *data_block, char *block_list)
+{
+       struct squashfs_sb_info *msblk = &inode->i_sb->u.squashfs_sb;
+       struct squashfs_super_block *sblk = &msblk->sblk;
+       int skip = calculate_skip(i_size_read(inode) >> sblk->block_log);
+       int offset = 0;
+       struct meta_index *meta;
+       struct meta_entry *meta_entry;
+       long long cur_index_block = SQUASHFS_I(inode)->u.s1.block_list_start;
+       int cur_offset = SQUASHFS_I(inode)->offset;
+       long long cur_data_block = SQUASHFS_I(inode)->start_block;
+       int i;
+       index /= SQUASHFS_META_INDEXES * skip;
+
+       while ( offset < index ) {
+               meta = locate_meta_index(inode, index, offset + 1);
+
+               if (meta == NULL) {
+                       if ((meta = empty_meta_index(inode, offset + 1,
+                                                       skip)) == NULL)
+                               goto all_done;
+               } else {
+                       offset = index < meta->offset + meta->entries ? index :
+                               meta->offset + meta->entries - 1;
+                       meta_entry = &meta->meta_entry[offset - meta->offset];
+                       cur_index_block = meta_entry->index_block + sblk->inode_table_start;
+                       cur_offset = meta_entry->offset;
+                       cur_data_block = meta_entry->data_block;
+                       TRACE("get_meta_index: offset %d, meta->offset %d, "
+                               "meta->entries %d\n", offset, meta->offset,
+                               meta->entries);
+                       TRACE("get_meta_index: index_block 0x%llx, offset 0x%x"
+                               " data_block 0x%llx\n", cur_index_block,
+                               cur_offset, cur_data_block);
+               }
+
+               for (i = meta->offset + meta->entries; i <= index &&
+                               i < meta->offset + SQUASHFS_META_ENTRIES; i++) {
+                       int blocks = skip * SQUASHFS_META_INDEXES;
+
+                       while (blocks) {
+                               int block = blocks > (SIZE >> 2) ? (SIZE >> 2) :
+                                       blocks;
+                               int res = read_block_index(inode->i_sb, block,
+                                       block_list, &cur_index_block,
+                                       &cur_offset);
+
+                               if (res == -1)
+                                       goto failed;
+
+                               cur_data_block += res;
+                               blocks -= block;
+                       }
+
+                       meta_entry = &meta->meta_entry[i - meta->offset];
+                       meta_entry->index_block = cur_index_block - sblk->inode_table_start;
+                       meta_entry->offset = cur_offset;
+                       meta_entry->data_block = cur_data_block;
+                       meta->entries ++;
+                       offset ++;
+               }
+
+               TRACE("get_meta_index: meta->offset %d, meta->entries %d\n",
+                               meta->offset, meta->entries);
+
+               release_meta_index(inode, meta);
+       }
+
+all_done:
+       *index_block = cur_index_block;
+       *index_offset = cur_offset;
+       *data_block = cur_data_block;
+
+       return offset * SQUASHFS_META_INDEXES * skip;
+
+failed:
+       release_meta_index(inode, meta);
+       return -1;
+}
+
+
+static long long read_blocklist(struct inode *inode, int index,
+                               int readahead_blks, char *block_list,
+                               unsigned short **block_p, unsigned int *bsize)
+{
+       long long block_ptr;
+       int offset;
+       long long block;
+       int res = get_meta_index(inode, index, &block_ptr, &offset, &block,
+               block_list);
+
+       TRACE("read_blocklist: res %d, index %d, block_ptr 0x%llx, offset"
+                      " 0x%x, block 0x%llx\n", res, index, block_ptr, offset,
+                      block);
+
+       if(res == -1)
+               goto failure;
+
+       index -= res;
+
+       while ( index ) {
+               int blocks = index > (SIZE >> 2) ? (SIZE >> 2) : index;
+               int res = read_block_index(inode->i_sb, blocks, block_list,
+                       &block_ptr, &offset);
+               if (res == -1)
+                       goto failure;
+               block += res;
+               index -= blocks;
+       }
+
+       if (read_block_index(inode->i_sb, 1, block_list,
+                       &block_ptr, &offset) == -1)
+               goto failure;
+       *bsize = *((unsigned int *) block_list);
+
+       return block;
+
+failure:
+       return 0;
+}
+
+
+static int squashfs_readpage(struct file *file, struct page *page)
+{
+       struct inode *inode = page->mapping->host;
+       struct squashfs_sb_info *msblk = &inode->i_sb->u.squashfs_sb;
+       struct squashfs_super_block *sblk = &msblk->sblk;
+       unsigned char block_list[SIZE];
+       long long block;
+       unsigned int bsize, i = 0, bytes = 0, byte_offset = 0;
+       int index = page->index >> (sblk->block_log - PAGE_CACHE_SHIFT);
+       void *pageaddr;
+       struct squashfs_fragment_cache *fragment = NULL;
+       char *data_ptr = msblk->read_page;
+       
+       int mask = (1 << (sblk->block_log - PAGE_CACHE_SHIFT)) - 1;
+       int start_index = page->index & ~mask;
+       int end_index = start_index | mask;
+
+       TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
+                                       page->index,
+                                       SQUASHFS_I(inode)->start_block);
+
+       if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
+                                       PAGE_CACHE_SHIFT))
+               goto skip_read;
+
+       if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
+                                       || index < (i_size_read(inode) >>
+                                       sblk->block_log)) {
+               if ((block = (msblk->read_blocklist)(inode, index, 1,
+                                       block_list, NULL, &bsize)) == 0)
+                       goto skip_read;
+
+               down(&msblk->read_page_mutex);
+               
+               if (!(bytes = squashfs_read_data(inode->i_sb, msblk->read_page,
+                                       block, bsize, NULL))) {
+                       ERROR("Unable to read page, block %llx, size %x\n", block,
+                                       bsize);
+                       up(&msblk->read_page_mutex);
+                       goto skip_read;
+               }
+       } else {
+               if ((fragment = get_cached_fragment(inode->i_sb,
+                                       SQUASHFS_I(inode)->
+                                       u.s1.fragment_start_block,
+                                       SQUASHFS_I(inode)->u.s1.fragment_size))
+                                       == NULL) {
+                       ERROR("Unable to read page, block %llx, size %x\n",
+                                       SQUASHFS_I(inode)->
+                                       u.s1.fragment_start_block,
+                                       (int) SQUASHFS_I(inode)->
+                                       u.s1.fragment_size);
+                       goto skip_read;
+               }
+               bytes = SQUASHFS_I(inode)->u.s1.fragment_offset +
+                                       (i_size_read(inode) & (sblk->block_size
+                                       - 1));
+               byte_offset = SQUASHFS_I(inode)->u.s1.fragment_offset;
+               data_ptr = fragment->data;
+       }
+
+       for (i = start_index; i <= end_index && byte_offset < bytes;
+                                       i++, byte_offset += PAGE_CACHE_SIZE) {
+               struct page *push_page;
+               int available_bytes = (bytes - byte_offset) > PAGE_CACHE_SIZE ?
+                                       PAGE_CACHE_SIZE : bytes - byte_offset;
+
+               TRACE("bytes %d, i %d, byte_offset %d, available_bytes %d\n",
+                                       bytes, i, byte_offset, available_bytes);
+
+               if (i == page->index)  {
+                       pageaddr = kmap_atomic(page, KM_USER0);
+                       memcpy(pageaddr, data_ptr + byte_offset,
+                                       available_bytes);
+                       memset(pageaddr + available_bytes, 0,
+                                       PAGE_CACHE_SIZE - available_bytes);
+                       kunmap_atomic(pageaddr, KM_USER0);
+                       flush_dcache_page(page);
+                       SetPageUptodate(page);
+                       UnlockPage(page);
+               } else if ((push_page =
+                               grab_cache_page_nowait(page->mapping, i))) {
+                       pageaddr = kmap_atomic(push_page, KM_USER0);
+
+                       memcpy(pageaddr, data_ptr + byte_offset,
+                                       available_bytes);
+                       memset(pageaddr + available_bytes, 0,
+                                       PAGE_CACHE_SIZE - available_bytes);
+                       kunmap_atomic(pageaddr, KM_USER0);
+                       flush_dcache_page(push_page);
+                       SetPageUptodate(push_page);
+                       UnlockPage(push_page);
+                       page_cache_release(push_page);
+               }
+       }
+
+       if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
+                                       || index < (i_size_read(inode) >>
+                                       sblk->block_log))
+               up(&msblk->read_page_mutex);
+       else
+               release_cached_fragment(msblk, fragment);
+
+       return 0;
+
+skip_read:
+       pageaddr = kmap_atomic(page, KM_USER0);
+       memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
+       kunmap_atomic(pageaddr, KM_USER0);
+       flush_dcache_page(page);
+       SetPageUptodate(page);
+       UnlockPage(page);
+
+       return 0;
+}
+
+
+static int squashfs_readpage4K(struct file *file, struct page *page)
+{
+       struct inode *inode = page->mapping->host;
+       struct squashfs_sb_info *msblk = &inode->i_sb->u.squashfs_sb;
+       struct squashfs_super_block *sblk = &msblk->sblk;
+       unsigned char block_list[SIZE];
+       long long block;
+       unsigned int bsize, bytes = 0;
+       void *pageaddr;
+       
+       TRACE("Entered squashfs_readpage4K, page index %lx, start block %llx\n",
+                                       page->index,
+                                       SQUASHFS_I(inode)->start_block);
+
+       if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
+                                       PAGE_CACHE_SHIFT)) {
+               pageaddr = kmap_atomic(page, KM_USER0);
+               goto skip_read;
+       }
+
+       if (SQUASHFS_I(inode)->u.s1.fragment_start_block == SQUASHFS_INVALID_BLK
+                                       || page->index < (i_size_read(inode) >>
+                                       sblk->block_log)) {
+               block = (msblk->read_blocklist)(inode, page->index, 1,
+                                       block_list, NULL, &bsize);
+
+               down(&msblk->read_page_mutex);
+               bytes = squashfs_read_data(inode->i_sb, msblk->read_page, block,
+                                       bsize, NULL);
+               pageaddr = kmap_atomic(page, KM_USER0);
+               if (bytes)
+                       memcpy(pageaddr, msblk->read_page, bytes);
+               else
+                       ERROR("Unable to read page, block %llx, size %x\n",
+                                       block, bsize);
+               up(&msblk->read_page_mutex);
+       } else {
+               struct squashfs_fragment_cache *fragment =
+                       get_cached_fragment(inode->i_sb,
+                                       SQUASHFS_I(inode)->
+                                       u.s1.fragment_start_block,
+                                       SQUASHFS_I(inode)-> u.s1.fragment_size);
+               pageaddr = kmap_atomic(page, KM_USER0);
+               if (fragment) {
+                       bytes = i_size_read(inode) & (sblk->block_size - 1);
+                       memcpy(pageaddr, fragment->data + SQUASHFS_I(inode)->
+                                       u.s1.fragment_offset, bytes);
+                       release_cached_fragment(msblk, fragment);
+               } else
+                       ERROR("Unable to read page, block %llx, size %x\n",
+                                       SQUASHFS_I(inode)->
+                                       u.s1.fragment_start_block, (int)
+                                       SQUASHFS_I(inode)-> u.s1.fragment_size);
+       }
+
+skip_read:
+       memset(pageaddr + bytes, 0, PAGE_CACHE_SIZE - bytes);
+       kunmap_atomic(pageaddr, KM_USER0);
+       flush_dcache_page(page);
+       SetPageUptodate(page);
+       UnlockPage(page);
+
+       return 0;
+}
+
+
+static int get_dir_index_using_offset(struct super_block *s, long long 
+                               *next_block, unsigned int *next_offset,
+                               long long index_start,
+                               unsigned int index_offset, int i_count,
+                               long long f_pos)
+{
+       struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+       struct squashfs_super_block *sblk = &msblk->sblk;
+       int i, length = 0;
+       struct squashfs_dir_index index;
+
+       TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
+                                       i_count, (unsigned int) f_pos);
+
+       f_pos -= 3;
+       if (f_pos == 0)
+               goto finish;
+
+       for (i = 0; i < i_count; i++) {
+               if (msblk->swap) {
+                       struct squashfs_dir_index sindex;
+                       squashfs_get_cached_block(s, (char *) &sindex,
+                                       index_start, index_offset,
+                                       sizeof(sindex), &index_start,
+                                       &index_offset);
+                       SQUASHFS_SWAP_DIR_INDEX(&index, &sindex);
+               } else
+                       squashfs_get_cached_block(s, (char *) &index,
+                                       index_start, index_offset,
+                                       sizeof(index), &index_start,
+                                       &index_offset);
+
+               if (index.index > f_pos)
+                       break;
+
+               squashfs_get_cached_block(s, NULL, index_start, index_offset,
+                                       index.size + 1, &index_start,
+                                       &index_offset);
+
+               length = index.index;
+               *next_block = index.start_block + sblk->directory_table_start;
+       }
+
+       *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
+
+finish:
+       return length + 3;
+}
+
+
+static int get_dir_index_using_name(struct super_block *s, long long
+                               *next_block, unsigned int *next_offset,
+                               long long index_start,
+                               unsigned int index_offset, int i_count,
+                               const char *name, int size)
+{
+       struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+       struct squashfs_super_block *sblk = &msblk->sblk;
+       int i, length = 0;
+       char buffer[sizeof(struct squashfs_dir_index) + SQUASHFS_NAME_LEN + 1];
+       struct squashfs_dir_index *index = (struct squashfs_dir_index *) buffer;
+       char str[SQUASHFS_NAME_LEN + 1];
+
+       TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
+
+       strncpy(str, name, size);
+       str[size] = '\0';
+
+       for (i = 0; i < i_count; i++) {
+               if (msblk->swap) {
+                       struct squashfs_dir_index sindex;
+                       squashfs_get_cached_block(s, (char *) &sindex,
+                                       index_start, index_offset,
+                                       sizeof(sindex), &index_start,
+                                       &index_offset);
+                       SQUASHFS_SWAP_DIR_INDEX(index, &sindex);
+               } else
+                       squashfs_get_cached_block(s, (char *) index,
+                                       index_start, index_offset,
+                                       sizeof(struct squashfs_dir_index),
+                                       &index_start, &index_offset);
+
+               squashfs_get_cached_block(s, index->name, index_start,
+                                       index_offset, index->size + 1,
+                                       &index_start, &index_offset);
+
+               index->name[index->size + 1] = '\0';
+
+               if (strcmp(index->name, str) > 0)
+                       break;
+
+               length = index->index;
+               *next_block = index->start_block + sblk->directory_table_start;
+       }
+
+       *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
+       return length + 3;
+}
+
+               
+static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
+{
+       struct inode *i = file->f_dentry->d_inode;
+       struct squashfs_sb_info *msblk = &i->i_sb->u.squashfs_sb;
+       struct squashfs_super_block *sblk = &msblk->sblk;
+       long long next_block = SQUASHFS_I(i)->start_block +
+               sblk->directory_table_start;
+       int next_offset = SQUASHFS_I(i)->offset, length = 0,
+               dir_count;
+       struct squashfs_dir_header dirh;
+       char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1];
+       struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer;
+
+       TRACE("Entered squashfs_readdir [%llx:%x]\n", next_block, next_offset);
+
+       while(file->f_pos < 3) {
+               char *name;
+               int size, i_ino;
+
+               if(file->f_pos == 0) {
+                       name = ".";
+                       size = 1;
+                       i_ino = i->i_ino;
+               } else {
+                       name = "..";
+                       size = 2;
+                       i_ino = SQUASHFS_I(i)->u.s2.parent_inode;
+               }
+               TRACE("Calling filldir(%x, %s, %d, %d, %d, %d)\n",
+                               (unsigned int) dirent, name, size, (int)
+                               file->f_pos, i_ino,
+                               squashfs_filetype_table[1]);
+
+               if (filldir(dirent, name, size,
+                               file->f_pos, i_ino,
+                               squashfs_filetype_table[1]) < 0) {
+                               TRACE("Filldir returned less than 0\n");
+                               goto finish;
+               }
+               file->f_pos += size;
+       }
+
+       length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset,
+                               SQUASHFS_I(i)->u.s2.directory_index_start,
+                               SQUASHFS_I(i)->u.s2.directory_index_offset,
+                               SQUASHFS_I(i)->u.s2.directory_index_count,
+                               file->f_pos);
+
+       while (length < i_size_read(i)) {
+               /* read directory header */
+               if (msblk->swap) {
+                       struct squashfs_dir_header sdirh;
+                       
+                       if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
+                                       next_block, next_offset, sizeof(sdirh),
+                                       &next_block, &next_offset))
+                               goto failed_read;
+
+                       length += sizeof(sdirh);
+                       SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
+               } else {
+                       if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
+                                       next_block, next_offset, sizeof(dirh),
+                                       &next_block, &next_offset))
+                               goto failed_read;
+
+                       length += sizeof(dirh);
+               }
+
+               dir_count = dirh.count + 1;
+               while (dir_count--) {
+                       if (msblk->swap) {
+                               struct squashfs_dir_entry sdire;
+                               if (!squashfs_get_cached_block(i->i_sb, (char *)
+                                               &sdire, next_block, next_offset,
+                                               sizeof(sdire), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+                               
+                               length += sizeof(sdire);
+                               SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
+                       } else {
+                               if (!squashfs_get_cached_block(i->i_sb, (char *)
+                                               dire, next_block, next_offset,
+                                               sizeof(*dire), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+
+                               length += sizeof(*dire);
+                       }
+
+                       if (!squashfs_get_cached_block(i->i_sb, dire->name,
+                                               next_block, next_offset,
+                                               dire->size + 1, &next_block,
+                                               &next_offset))
+                               goto failed_read;
+
+                       length += dire->size + 1;
+
+                       if (file->f_pos >= length)
+                               continue;
+
+                       dire->name[dire->size + 1] = '\0';
+
+                       TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d, %d)\n",
+                                       (unsigned int) dirent, dire->name,
+                                       dire->size + 1, (int) file->f_pos,
+                                       dirh.start_block, dire->offset,
+                                       dirh.inode_number + dire->inode_number,
+                                       squashfs_filetype_table[dire->type]);
+
+                       if (filldir(dirent, dire->name, dire->size + 1,
+                                       file->f_pos,
+                                       dirh.inode_number + dire->inode_number,
+                                       squashfs_filetype_table[dire->type])
+                                       < 0) {
+                               TRACE("Filldir returned less than 0\n");
+                               goto finish;
+                       }
+                       file->f_pos = length;
+               }
+       }
+
+finish:
+       return 0;
+
+failed_read:
+       ERROR("Unable to read directory block [%llx:%x]\n", next_block,
+               next_offset);
+       return 0;
+}
+
+
+static struct dentry *squashfs_lookup(struct inode *i, struct dentry *dentry)
+{
+       const unsigned char *name = dentry->d_name.name;
+       int len = dentry->d_name.len;
+       struct inode *inode = NULL;
+       struct squashfs_sb_info *msblk = &i->i_sb->u.squashfs_sb;
+       struct squashfs_super_block *sblk = &msblk->sblk;
+       long long next_block = SQUASHFS_I(i)->start_block +
+                               sblk->directory_table_start;
+       int next_offset = SQUASHFS_I(i)->offset, length = 0,
+                               dir_count;
+       struct squashfs_dir_header dirh;
+       char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN];
+       struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer;
+
+       TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset);
+
+       if (len > SQUASHFS_NAME_LEN)
+               goto exit_loop;
+
+       length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
+                               SQUASHFS_I(i)->u.s2.directory_index_start,
+                               SQUASHFS_I(i)->u.s2.directory_index_offset,
+                               SQUASHFS_I(i)->u.s2.directory_index_count, name,
+                               len);
+
+       while (length < i_size_read(i)) {
+               /* read directory header */
+               if (msblk->swap) {
+                       struct squashfs_dir_header sdirh;
+                       if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
+                                       next_block, next_offset, sizeof(sdirh),
+                                       &next_block, &next_offset))
+                               goto failed_read;
+
+                       length += sizeof(sdirh);
+                       SQUASHFS_SWAP_DIR_HEADER(&dirh, &sdirh);
+               } else {
+                       if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
+                                       next_block, next_offset, sizeof(dirh),
+                                       &next_block, &next_offset))
+                               goto failed_read;
+
+                       length += sizeof(dirh);
+               }
+
+               dir_count = dirh.count + 1;
+               while (dir_count--) {
+                       if (msblk->swap) {
+                               struct squashfs_dir_entry sdire;
+                               if (!squashfs_get_cached_block(i->i_sb, (char *)
+                                               &sdire, next_block,next_offset,
+                                               sizeof(sdire), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+                               
+                               length += sizeof(sdire);
+                               SQUASHFS_SWAP_DIR_ENTRY(dire, &sdire);
+                       } else {
+                               if (!squashfs_get_cached_block(i->i_sb, (char *)
+                                               dire, next_block,next_offset,
+                                               sizeof(*dire), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+
+                               length += sizeof(*dire);
+                       }
+
+                       if (!squashfs_get_cached_block(i->i_sb, dire->name,
+                                       next_block, next_offset, dire->size + 1,
+                                       &next_block, &next_offset))
+                               goto failed_read;
+
+                       length += dire->size + 1;
+
+                       if (name[0] < dire->name[0])
+                               goto exit_loop;
+
+                       if ((len == dire->size + 1) && !strncmp(name,
+                                               dire->name, len)) {
+                               squashfs_inode_t ino =
+                                       SQUASHFS_MKINODE(dirh.start_block,
+                                       dire->offset);
+
+                               TRACE("calling squashfs_iget for directory "
+                                       "entry %s, inode %x:%x, %d\n", name,
+                                       dirh.start_block, dire->offset,
+                                       dirh.inode_number + dire->inode_number);
+
+                               inode = (msblk->iget)(i->i_sb, ino);
+
+                               goto exit_loop;
+                       }
+               }
+       }
+
+exit_loop:
+       d_add(dentry, inode);
+       return ERR_PTR(0);
+
+failed_read:
+       ERROR("Unable to read directory block [%llx:%x]\n", next_block,
+               next_offset);
+       goto exit_loop;
+}
+
+
+static void squashfs_put_super(struct super_block *s)
+{
+       int i;
+
+               struct squashfs_sb_info *sbi = &s->u.squashfs_sb;
+               if (sbi->block_cache)
+                       for (i = 0; i < SQUASHFS_CACHED_BLKS; i++)
+                               if (sbi->block_cache[i].block !=
+                                                       SQUASHFS_INVALID_BLK)
+                                       kfree(sbi->block_cache[i].data);
+               if (sbi->fragment)
+                       for (i = 0; i < SQUASHFS_CACHED_FRAGMENTS; i++) 
+                               SQUASHFS_FREE(sbi->fragment[i].data);
+               kfree(sbi->fragment);
+               kfree(sbi->block_cache);
+               kfree(sbi->read_data);
+               kfree(sbi->read_page);
+               kfree(sbi->uid);
+               kfree(sbi->fragment_index);
+               kfree(sbi->fragment_index_2);
+               kfree(sbi->meta_index);
+               vfree(sbi->stream.workspace);
+               sbi->block_cache = NULL;
+               sbi->uid = NULL;
+               sbi->read_data = NULL;
+               sbi->read_page = NULL;
+               sbi->fragment = NULL;
+               sbi->fragment_index = NULL;
+               sbi->fragment_index_2 = NULL;
+               sbi->meta_index = NULL;
+               sbi->stream.workspace = NULL;
+}
+
+
+static int __init init_squashfs_fs(void)
+{
+
+       printk(KERN_INFO "squashfs: version 3.1 (2006/08/15) "
+               "Phillip Lougher\n");
+
+       return register_filesystem(&squashfs_fs_type);
+}
+
+
+static void __exit exit_squashfs_fs(void)
+{
+       unregister_filesystem(&squashfs_fs_type);
+}
+
+
+EXPORT_NO_SYMBOLS;
+
+module_init(init_squashfs_fs);
+module_exit(exit_squashfs_fs);
+MODULE_DESCRIPTION("squashfs 3.1, a compressed read-only filesystem");
+MODULE_AUTHOR("Phillip Lougher <phillip@lougher.demon.co.uk>");
+MODULE_LICENSE("GPL");
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/squashfs.h b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/squashfs.h
new file mode 100644 (file)
index 0000000..b5f93fb
--- /dev/null
@@ -0,0 +1,85 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * squashfs.h
+ */
+
+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
+#undef CONFIG_SQUASHFS_1_0_COMPATIBILITY
+#endif
+#ifdef SQUASHFS_TRACE
+#define TRACE(s, args...)      printk(KERN_NOTICE "SQUASHFS: "s, ## args)
+#else
+#define TRACE(s, args...)      {}
+#endif
+
+#define ERROR(s, args...)      printk(KERN_ERR "SQUASHFS error: "s, ## args)
+
+#define SERROR(s, args...)     do { \
+                               if (!silent) \
+                               printk(KERN_ERR "SQUASHFS error: "s, ## args);\
+                               } while(0)
+
+#define WARNING(s, args...)    printk(KERN_WARNING "SQUASHFS: "s, ## args)
+
+#define SQUASHFS_I(INO)                        (&INO->u.squashfs_i)
+
+#define i_size_read(INO)               (INO->i_size)
+
+#if defined(CONFIG_SQUASHFS_1_0_COMPATIBILITY ) || defined(CONFIG_SQUASHFS_2_0_COMPATIBILITY)
+#define SQSH_EXTERN
+extern unsigned int squashfs_read_data(struct super_block *s, char *buffer,
+                               long long index, unsigned int length,
+                               long long *next_index);
+extern int squashfs_get_cached_block(struct super_block *s, char *buffer,
+                               long long block, unsigned int offset,
+                               int length, long long *next_block,
+                               unsigned int *next_offset);
+extern void release_cached_fragment(struct squashfs_sb_info *msblk, struct
+                                       squashfs_fragment_cache *fragment);
+extern struct squashfs_fragment_cache *get_cached_fragment(struct super_block
+                                       *s, long long start_block,
+                                       int length);
+extern struct address_space_operations squashfs_symlink_aops;
+extern struct address_space_operations squashfs_aops;
+extern struct address_space_operations squashfs_aops_4K;
+extern struct file_operations squashfs_dir_ops;
+extern struct inode_operations squashfs_dir_inode_ops;
+#else
+#define SQSH_EXTERN static
+#endif
+
+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
+extern int squashfs_1_0_supported(struct squashfs_sb_info *msblk);
+#else
+static inline int squashfs_1_0_supported(struct squashfs_sb_info *msblk)
+{
+       return 0;
+}
+#endif
+
+#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
+extern int squashfs_2_0_supported(struct squashfs_sb_info *msblk);
+#else
+static inline int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
+{
+       return 0;
+}
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/squashfs2_0.c b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/fs/squashfs/squashfs2_0.c
new file mode 100644 (file)
index 0000000..6411a04
--- /dev/null
@@ -0,0 +1,751 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * squashfs2_0.c
+ */
+
+#include <linux/types.h>
+#include <linux/squashfs_fs.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/slab.h>
+#include <linux/zlib.h>
+#include <linux/fs.h>
+#include <linux/smp_lock.h>
+#include <linux/locks.h>
+#include <linux/init.h>
+#include <linux/dcache.h>
+#include <linux/wait.h>
+#include <linux/zlib.h>
+#include <linux/blkdev.h>
+#include <linux/vmalloc.h>
+#include <asm/uaccess.h>
+#include <asm/semaphore.h>
+#include "squashfs.h"
+
+static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir);
+static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry);
+
+static struct file_operations squashfs_dir_ops_2 = {
+       .read = generic_read_dir,
+       .readdir = squashfs_readdir_2
+};
+
+static struct inode_operations squashfs_dir_inode_ops_2 = {
+       .lookup = squashfs_lookup_2
+};
+
+static unsigned char squashfs_filetype_table[] = {
+       DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
+};
+
+static int read_fragment_index_table_2(struct super_block *s)
+{
+       struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+       struct squashfs_super_block *sblk = &msblk->sblk;
+
+       if (!(msblk->fragment_index_2 = kmalloc(SQUASHFS_FRAGMENT_INDEX_BYTES_2
+                                       (sblk->fragments), GFP_KERNEL))) {
+               ERROR("Failed to allocate uid/gid table\n");
+               return 0;
+       }
+   
+       if (SQUASHFS_FRAGMENT_INDEX_BYTES_2(sblk->fragments) &&
+                                       !squashfs_read_data(s, (char *)
+                                       msblk->fragment_index_2,
+                                       sblk->fragment_table_start,
+                                       SQUASHFS_FRAGMENT_INDEX_BYTES_2
+                                       (sblk->fragments) |
+                                       SQUASHFS_COMPRESSED_BIT_BLOCK, NULL)) {
+               ERROR("unable to read fragment index table\n");
+               return 0;
+       }
+
+       if (msblk->swap) {
+               int i;
+               unsigned int fragment;
+
+               for (i = 0; i < SQUASHFS_FRAGMENT_INDEXES_2(sblk->fragments);
+                                                                       i++) {
+                       SQUASHFS_SWAP_FRAGMENT_INDEXES_2((&fragment),
+                                               &msblk->fragment_index_2[i], 1);
+                       msblk->fragment_index_2[i] = fragment;
+               }
+       }
+
+       return 1;
+}
+
+
+static int get_fragment_location_2(struct super_block *s, unsigned int fragment,
+                               long long *fragment_start_block,
+                               unsigned int *fragment_size)
+{
+       struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+       long long start_block =
+               msblk->fragment_index_2[SQUASHFS_FRAGMENT_INDEX_2(fragment)];
+       int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET_2(fragment);
+       struct squashfs_fragment_entry_2 fragment_entry;
+
+       if (msblk->swap) {
+               struct squashfs_fragment_entry_2 sfragment_entry;
+
+               if (!squashfs_get_cached_block(s, (char *) &sfragment_entry,
+                                       start_block, offset,
+                                       sizeof(sfragment_entry), &start_block,
+                                       &offset))
+                       goto out;
+               SQUASHFS_SWAP_FRAGMENT_ENTRY_2(&fragment_entry, &sfragment_entry);
+       } else
+               if (!squashfs_get_cached_block(s, (char *) &fragment_entry,
+                                       start_block, offset,
+                                       sizeof(fragment_entry), &start_block,
+                                       &offset))
+                       goto out;
+
+       *fragment_start_block = fragment_entry.start_block;
+       *fragment_size = fragment_entry.size;
+
+       return 1;
+
+out:
+       return 0;
+}
+
+
+static struct inode *squashfs_new_inode(struct super_block *s,
+               struct squashfs_base_inode_header_2 *inodeb, unsigned int ino)
+{
+       struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+       struct squashfs_super_block *sblk = &msblk->sblk;
+       struct inode *i = new_inode(s);
+
+       if (i) {
+               i->i_ino = ino;
+               i->i_mtime = sblk->mkfs_time;
+               i->i_atime = sblk->mkfs_time;
+               i->i_ctime = sblk->mkfs_time;
+               i->i_uid = msblk->uid[inodeb->uid];
+               i->i_mode = inodeb->mode;
+               i->i_nlink = 1;
+               i->i_size = 0;
+               if (inodeb->guid == SQUASHFS_GUIDS)
+                       i->i_gid = i->i_uid;
+               else
+                       i->i_gid = msblk->guid[inodeb->guid];
+       }
+
+       return i;
+}
+
+
+static struct inode *squashfs_iget_2(struct super_block *s, squashfs_inode_t inode)
+{
+       struct inode *i;
+       struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+       struct squashfs_super_block *sblk = &msblk->sblk;
+       unsigned int block = SQUASHFS_INODE_BLK(inode) +
+               sblk->inode_table_start;
+       unsigned int offset = SQUASHFS_INODE_OFFSET(inode);
+       unsigned int ino = SQUASHFS_MK_VFS_INODE(block
+               - sblk->inode_table_start, offset);
+       long long next_block;
+       unsigned int next_offset;
+       union squashfs_inode_header_2 id, sid;
+       struct squashfs_base_inode_header_2 *inodeb = &id.base,
+                                         *sinodeb = &sid.base;
+
+       TRACE("Entered squashfs_iget\n");
+
+       if (msblk->swap) {
+               if (!squashfs_get_cached_block(s, (char *) sinodeb, block,
+                                       offset, sizeof(*sinodeb), &next_block,
+                                       &next_offset))
+                       goto failed_read;
+               SQUASHFS_SWAP_BASE_INODE_HEADER_2(inodeb, sinodeb,
+                                       sizeof(*sinodeb));
+       } else
+               if (!squashfs_get_cached_block(s, (char *) inodeb, block,
+                                       offset, sizeof(*inodeb), &next_block,
+                                       &next_offset))
+                       goto failed_read;
+
+       switch(inodeb->inode_type) {
+               case SQUASHFS_FILE_TYPE: {
+                       struct squashfs_reg_inode_header_2 *inodep = &id.reg;
+                       struct squashfs_reg_inode_header_2 *sinodep = &sid.reg;
+                       long long frag_blk;
+                       unsigned int frag_size;
+                               
+                       if (msblk->swap) {
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               sinodep, block, offset,
+                                               sizeof(*sinodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+                               SQUASHFS_SWAP_REG_INODE_HEADER_2(inodep, sinodep);
+                       } else
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               inodep, block, offset,
+                                               sizeof(*inodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+
+                       frag_blk = SQUASHFS_INVALID_BLK;
+                       if (inodep->fragment != SQUASHFS_INVALID_FRAG &&
+                                       !get_fragment_location_2(s,
+                                       inodep->fragment, &frag_blk, &frag_size))
+                               goto failed_read;
+                               
+                       if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
+                               goto failed_read1;
+
+                       i->i_size = inodep->file_size;
+                       i->i_fop = &generic_ro_fops;
+                       i->i_mode |= S_IFREG;
+                       i->i_mtime = inodep->mtime;
+                       i->i_atime = inodep->mtime;
+                       i->i_ctime = inodep->mtime;
+                       i->i_blocks = ((i->i_size - 1) >> 9) + 1;
+                       i->i_blksize = PAGE_CACHE_SIZE;
+                       SQUASHFS_I(i)->u.s1.fragment_start_block = frag_blk;
+                       SQUASHFS_I(i)->u.s1.fragment_size = frag_size;
+                       SQUASHFS_I(i)->u.s1.fragment_offset = inodep->offset;
+                       SQUASHFS_I(i)->start_block = inodep->start_block;
+                       SQUASHFS_I(i)->u.s1.block_list_start = next_block;
+                       SQUASHFS_I(i)->offset = next_offset;
+                       if (sblk->block_size > 4096)
+                               i->i_data.a_ops = &squashfs_aops;
+                       else
+                               i->i_data.a_ops = &squashfs_aops_4K;
+
+                       TRACE("File inode %x:%x, start_block %x, "
+                                       "block_list_start %llx, offset %x\n",
+                                       SQUASHFS_INODE_BLK(inode), offset,
+                                       inodep->start_block, next_block,
+                                       next_offset);
+                       break;
+               }
+               case SQUASHFS_DIR_TYPE: {
+                       struct squashfs_dir_inode_header_2 *inodep = &id.dir;
+                       struct squashfs_dir_inode_header_2 *sinodep = &sid.dir;
+
+                       if (msblk->swap) {
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               sinodep, block, offset,
+                                               sizeof(*sinodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+                               SQUASHFS_SWAP_DIR_INODE_HEADER_2(inodep, sinodep);
+                       } else
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               inodep, block, offset,
+                                               sizeof(*inodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+
+                       if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
+                               goto failed_read1;
+
+                       i->i_size = inodep->file_size;
+                       i->i_op = &squashfs_dir_inode_ops_2;
+                       i->i_fop = &squashfs_dir_ops_2;
+                       i->i_mode |= S_IFDIR;
+                       i->i_mtime = inodep->mtime;
+                       i->i_atime = inodep->mtime;
+                       i->i_ctime = inodep->mtime;
+                       SQUASHFS_I(i)->start_block = inodep->start_block;
+                       SQUASHFS_I(i)->offset = inodep->offset;
+                       SQUASHFS_I(i)->u.s2.directory_index_count = 0;
+                       SQUASHFS_I(i)->u.s2.parent_inode = 0;
+
+                       TRACE("Directory inode %x:%x, start_block %x, offset "
+                                       "%x\n", SQUASHFS_INODE_BLK(inode),
+                                       offset, inodep->start_block,
+                                       inodep->offset);
+                       break;
+               }
+               case SQUASHFS_LDIR_TYPE: {
+                       struct squashfs_ldir_inode_header_2 *inodep = &id.ldir;
+                       struct squashfs_ldir_inode_header_2 *sinodep = &sid.ldir;
+
+                       if (msblk->swap) {
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               sinodep, block, offset,
+                                               sizeof(*sinodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+                               SQUASHFS_SWAP_LDIR_INODE_HEADER_2(inodep,
+                                               sinodep);
+                       } else
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               inodep, block, offset,
+                                               sizeof(*inodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+
+                       if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
+                               goto failed_read1;
+
+                       i->i_size = inodep->file_size;
+                       i->i_op = &squashfs_dir_inode_ops_2;
+                       i->i_fop = &squashfs_dir_ops_2;
+                       i->i_mode |= S_IFDIR;
+                       i->i_mtime = inodep->mtime;
+                       i->i_atime = inodep->mtime;
+                       i->i_ctime = inodep->mtime;
+                       SQUASHFS_I(i)->start_block = inodep->start_block;
+                       SQUASHFS_I(i)->offset = inodep->offset;
+                       SQUASHFS_I(i)->u.s2.directory_index_start = next_block;
+                       SQUASHFS_I(i)->u.s2.directory_index_offset =
+                                                               next_offset;
+                       SQUASHFS_I(i)->u.s2.directory_index_count =
+                                                               inodep->i_count;
+                       SQUASHFS_I(i)->u.s2.parent_inode = 0;
+
+                       TRACE("Long directory inode %x:%x, start_block %x, "
+                                       "offset %x\n",
+                                       SQUASHFS_INODE_BLK(inode), offset,
+                                       inodep->start_block, inodep->offset);
+                       break;
+               }
+               case SQUASHFS_SYMLINK_TYPE: {
+                       struct squashfs_symlink_inode_header_2 *inodep =
+                                                               &id.symlink;
+                       struct squashfs_symlink_inode_header_2 *sinodep =
+                                                               &sid.symlink;
+       
+                       if (msblk->swap) {
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               sinodep, block, offset,
+                                               sizeof(*sinodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+                               SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep,
+                                                               sinodep);
+                       } else
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               inodep, block, offset,
+                                               sizeof(*inodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+
+                       if((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
+                               goto failed_read1;
+
+                       i->i_size = inodep->symlink_size;
+                       i->i_op = &page_symlink_inode_operations;
+                       i->i_data.a_ops = &squashfs_symlink_aops;
+                       i->i_mode |= S_IFLNK;
+                       SQUASHFS_I(i)->start_block = next_block;
+                       SQUASHFS_I(i)->offset = next_offset;
+
+                       TRACE("Symbolic link inode %x:%x, start_block %llx, "
+                                       "offset %x\n",
+                                       SQUASHFS_INODE_BLK(inode), offset,
+                                       next_block, next_offset);
+                       break;
+                }
+                case SQUASHFS_BLKDEV_TYPE:
+                case SQUASHFS_CHRDEV_TYPE: {
+                       struct squashfs_dev_inode_header_2 *inodep = &id.dev;
+                       struct squashfs_dev_inode_header_2 *sinodep = &sid.dev;
+
+                       if (msblk->swap) {
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               sinodep, block, offset,
+                                               sizeof(*sinodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+                               SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep, sinodep);
+                       } else  
+                               if (!squashfs_get_cached_block(s, (char *)
+                                               inodep, block, offset,
+                                               sizeof(*inodep), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+
+                       if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
+                               goto failed_read1;
+
+                       i->i_mode |= (inodeb->inode_type ==
+                                       SQUASHFS_CHRDEV_TYPE) ?  S_IFCHR :
+                                       S_IFBLK;
+                       init_special_inode(i, i->i_mode, inodep->rdev);
+
+                       TRACE("Device inode %x:%x, rdev %x\n",
+                                       SQUASHFS_INODE_BLK(inode), offset,
+                                       inodep->rdev);
+                       break;
+                }
+                case SQUASHFS_FIFO_TYPE:
+                case SQUASHFS_SOCKET_TYPE: {
+                       if ((i = squashfs_new_inode(s, inodeb, ino)) == NULL)
+                               goto failed_read1;
+
+                       i->i_mode |= (inodeb->inode_type == SQUASHFS_FIFO_TYPE)
+                                                       ? S_IFIFO : S_IFSOCK;
+                       init_special_inode(i, i->i_mode, 0);
+                       break;
+                }
+                default:
+                       ERROR("Unknown inode type %d in squashfs_iget!\n",
+                                       inodeb->inode_type);
+                       goto failed_read1;
+       }
+       
+       insert_inode_hash(i);
+       return i;
+
+failed_read:
+       ERROR("Unable to read inode [%x:%x]\n", block, offset);
+
+failed_read1:
+       return NULL;
+}
+
+
+static int get_dir_index_using_offset(struct super_block *s, long long 
+                               *next_block, unsigned int *next_offset,
+                               long long index_start,
+                               unsigned int index_offset, int i_count,
+                               long long f_pos)
+{
+       struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+       struct squashfs_super_block *sblk = &msblk->sblk;
+       int i, length = 0;
+       struct squashfs_dir_index_2 index;
+
+       TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %d\n",
+                                       i_count, (unsigned int) f_pos);
+
+       if (f_pos == 0)
+               goto finish;
+
+       for (i = 0; i < i_count; i++) {
+               if (msblk->swap) {
+                       struct squashfs_dir_index_2 sindex;
+                       squashfs_get_cached_block(s, (char *) &sindex,
+                                       index_start, index_offset,
+                                       sizeof(sindex), &index_start,
+                                       &index_offset);
+                       SQUASHFS_SWAP_DIR_INDEX_2(&index, &sindex);
+               } else
+                       squashfs_get_cached_block(s, (char *) &index,
+                                       index_start, index_offset,
+                                       sizeof(index), &index_start,
+                                       &index_offset);
+
+               if (index.index > f_pos)
+                       break;
+
+               squashfs_get_cached_block(s, NULL, index_start, index_offset,
+                                       index.size + 1, &index_start,
+                                       &index_offset);
+
+               length = index.index;
+               *next_block = index.start_block + sblk->directory_table_start;
+       }
+
+       *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
+
+finish:
+       return length;
+}
+
+
+static int get_dir_index_using_name(struct super_block *s, long long
+                               *next_block, unsigned int *next_offset,
+                               long long index_start,
+                               unsigned int index_offset, int i_count,
+                               const char *name, int size)
+{
+       struct squashfs_sb_info *msblk = &s->u.squashfs_sb;
+       struct squashfs_super_block *sblk = &msblk->sblk;
+       int i, length = 0;
+       char buffer[sizeof(struct squashfs_dir_index_2) + SQUASHFS_NAME_LEN + 1];
+       struct squashfs_dir_index_2 *index = (struct squashfs_dir_index_2 *) buffer;
+       char str[SQUASHFS_NAME_LEN + 1];
+
+       TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
+
+       strncpy(str, name, size);
+       str[size] = '\0';
+
+       for (i = 0; i < i_count; i++) {
+               if (msblk->swap) {
+                       struct squashfs_dir_index_2 sindex;
+                       squashfs_get_cached_block(s, (char *) &sindex,
+                                       index_start, index_offset,
+                                       sizeof(sindex), &index_start,
+                                       &index_offset);
+                       SQUASHFS_SWAP_DIR_INDEX_2(index, &sindex);
+               } else
+                       squashfs_get_cached_block(s, (char *) index,
+                                       index_start, index_offset,
+                                       sizeof(struct squashfs_dir_index_2),
+                                       &index_start, &index_offset);
+
+               squashfs_get_cached_block(s, index->name, index_start,
+                                       index_offset, index->size + 1,
+                                       &index_start, &index_offset);
+
+               index->name[index->size + 1] = '\0';
+
+               if (strcmp(index->name, str) > 0)
+                       break;
+
+               length = index->index;
+               *next_block = index->start_block + sblk->directory_table_start;
+       }
+
+       *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
+       return length;
+}
+
+               
+static int squashfs_readdir_2(struct file *file, void *dirent, filldir_t filldir)
+{
+       struct inode *i = file->f_dentry->d_inode;
+       struct squashfs_sb_info *msblk = &i->i_sb->u.squashfs_sb;
+       struct squashfs_super_block *sblk = &msblk->sblk;
+       long long next_block = SQUASHFS_I(i)->start_block +
+               sblk->directory_table_start;
+       int next_offset = SQUASHFS_I(i)->offset, length = 0,
+               dir_count;
+       struct squashfs_dir_header_2 dirh;
+       char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1];
+       struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer;
+
+       TRACE("Entered squashfs_readdir_2 [%llx:%x]\n", next_block, next_offset);
+
+       length = get_dir_index_using_offset(i->i_sb, &next_block, &next_offset,
+                               SQUASHFS_I(i)->u.s2.directory_index_start,
+                               SQUASHFS_I(i)->u.s2.directory_index_offset,
+                               SQUASHFS_I(i)->u.s2.directory_index_count,
+                               file->f_pos);
+
+       while (length < i_size_read(i)) {
+               /* read directory header */
+               if (msblk->swap) {
+                       struct squashfs_dir_header_2 sdirh;
+                       
+                       if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
+                                       next_block, next_offset, sizeof(sdirh),
+                                       &next_block, &next_offset))
+                               goto failed_read;
+
+                       length += sizeof(sdirh);
+                       SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
+               } else {
+                       if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
+                                       next_block, next_offset, sizeof(dirh),
+                                       &next_block, &next_offset))
+                               goto failed_read;
+
+                       length += sizeof(dirh);
+               }
+
+               dir_count = dirh.count + 1;
+               while (dir_count--) {
+                       if (msblk->swap) {
+                               struct squashfs_dir_entry_2 sdire;
+                               if (!squashfs_get_cached_block(i->i_sb, (char *)
+                                               &sdire, next_block, next_offset,
+                                               sizeof(sdire), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+                               
+                               length += sizeof(sdire);
+                               SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
+                       } else {
+                               if (!squashfs_get_cached_block(i->i_sb, (char *)
+                                               dire, next_block, next_offset,
+                                               sizeof(*dire), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+
+                               length += sizeof(*dire);
+                       }
+
+                       if (!squashfs_get_cached_block(i->i_sb, dire->name,
+                                               next_block, next_offset,
+                                               dire->size + 1, &next_block,
+                                               &next_offset))
+                               goto failed_read;
+
+                       length += dire->size + 1;
+
+                       if (file->f_pos >= length)
+                               continue;
+
+                       dire->name[dire->size + 1] = '\0';
+
+                       TRACE("Calling filldir(%x, %s, %d, %d, %x:%x, %d)\n",
+                                       (unsigned int) dirent, dire->name,
+                                       dire->size + 1, (int) file->f_pos,
+                                       dirh.start_block, dire->offset,
+                                       squashfs_filetype_table[dire->type]);
+
+                       if (filldir(dirent, dire->name, dire->size + 1,
+                                       file->f_pos, SQUASHFS_MK_VFS_INODE(
+                                       dirh.start_block, dire->offset),
+                                       squashfs_filetype_table[dire->type])
+                                       < 0) {
+                               TRACE("Filldir returned less than 0\n");
+                               goto finish;
+                       }
+                       file->f_pos = length;
+               }
+       }
+
+finish:
+       return 0;
+
+failed_read:
+       ERROR("Unable to read directory block [%llx:%x]\n", next_block,
+               next_offset);
+       return 0;
+}
+
+
+static struct dentry *squashfs_lookup_2(struct inode *i, struct dentry *dentry)
+{
+       const unsigned char *name = dentry->d_name.name;
+       int len = dentry->d_name.len;
+       struct inode *inode = NULL;
+       struct squashfs_sb_info *msblk = &i->i_sb->u.squashfs_sb;
+       struct squashfs_super_block *sblk = &msblk->sblk;
+       long long next_block = SQUASHFS_I(i)->start_block +
+                               sblk->directory_table_start;
+       int next_offset = SQUASHFS_I(i)->offset, length = 0,
+                               dir_count;
+       struct squashfs_dir_header_2 dirh;
+       char buffer[sizeof(struct squashfs_dir_entry_2) + SQUASHFS_NAME_LEN];
+       struct squashfs_dir_entry_2 *dire = (struct squashfs_dir_entry_2 *) buffer;
+       int sorted = sblk->s_major == 2 && sblk->s_minor >= 1;
+
+       TRACE("Entered squashfs_lookup [%llx:%x]\n", next_block, next_offset);
+
+       if (len > SQUASHFS_NAME_LEN)
+               goto exit_loop;
+
+       length = get_dir_index_using_name(i->i_sb, &next_block, &next_offset,
+                               SQUASHFS_I(i)->u.s2.directory_index_start,
+                               SQUASHFS_I(i)->u.s2.directory_index_offset,
+                               SQUASHFS_I(i)->u.s2.directory_index_count, name,
+                               len);
+
+       while (length < i_size_read(i)) {
+               /* read directory header */
+               if (msblk->swap) {
+                       struct squashfs_dir_header_2 sdirh;
+                       if (!squashfs_get_cached_block(i->i_sb, (char *) &sdirh,
+                                       next_block, next_offset, sizeof(sdirh),
+                                       &next_block, &next_offset))
+                               goto failed_read;
+
+                       length += sizeof(sdirh);
+                       SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
+               } else {
+                       if (!squashfs_get_cached_block(i->i_sb, (char *) &dirh,
+                                       next_block, next_offset, sizeof(dirh),
+                                       &next_block, &next_offset))
+                               goto failed_read;
+
+                       length += sizeof(dirh);
+               }
+
+               dir_count = dirh.count + 1;
+               while (dir_count--) {
+                       if (msblk->swap) {
+                               struct squashfs_dir_entry_2 sdire;
+                               if (!squashfs_get_cached_block(i->i_sb, (char *)
+                                               &sdire, next_block,next_offset,
+                                               sizeof(sdire), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+                               
+                               length += sizeof(sdire);
+                               SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
+                       } else {
+                               if (!squashfs_get_cached_block(i->i_sb, (char *)
+                                               dire, next_block,next_offset,
+                                               sizeof(*dire), &next_block,
+                                               &next_offset))
+                                       goto failed_read;
+
+                               length += sizeof(*dire);
+                       }
+
+                       if (!squashfs_get_cached_block(i->i_sb, dire->name,
+                                       next_block, next_offset, dire->size + 1,
+                                       &next_block, &next_offset))
+                               goto failed_read;
+
+                       length += dire->size + 1;
+
+                       if (sorted && name[0] < dire->name[0])
+                               goto exit_loop;
+
+                       if ((len == dire->size + 1) && !strncmp(name,
+                                               dire->name, len)) {
+                               squashfs_inode_t ino =
+                                       SQUASHFS_MKINODE(dirh.start_block,
+                                       dire->offset);
+
+                               TRACE("calling squashfs_iget for directory "
+                                       "entry %s, inode %x:%x, %d\n", name,
+                                       dirh.start_block, dire->offset, ino);
+
+                               inode = (msblk->iget)(i->i_sb, ino);
+
+                               goto exit_loop;
+                       }
+               }
+       }
+
+exit_loop:
+       d_add(dentry, inode);
+       return ERR_PTR(0);
+
+failed_read:
+       ERROR("Unable to read directory block [%llx:%x]\n", next_block,
+               next_offset);
+       goto exit_loop;
+}
+
+
+int squashfs_2_0_supported(struct squashfs_sb_info *msblk)
+{
+       struct squashfs_super_block *sblk = &msblk->sblk;
+
+       msblk->iget = squashfs_iget_2;
+       msblk->read_fragment_index_table = read_fragment_index_table_2;
+
+       sblk->bytes_used = sblk->bytes_used_2;
+       sblk->uid_start = sblk->uid_start_2;
+       sblk->guid_start = sblk->guid_start_2;
+       sblk->inode_table_start = sblk->inode_table_start_2;
+       sblk->directory_table_start = sblk->directory_table_start_2;
+       sblk->fragment_table_start = sblk->fragment_table_start_2;
+
+       return 1;
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/include/linux/squashfs_fs.h b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/include/linux/squashfs_fs.h
new file mode 100644 (file)
index 0000000..eed48c3
--- /dev/null
@@ -0,0 +1,915 @@
+#ifndef SQUASHFS_FS
+#define SQUASHFS_FS
+
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * squashfs_fs.h
+ */
+
+#ifndef CONFIG_SQUASHFS_2_0_COMPATIBILITY
+#define CONFIG_SQUASHFS_2_0_COMPATIBILITY
+#endif
+
+#ifdef CONFIG_SQUASHFS_VMALLOC
+#define SQUASHFS_ALLOC(a)              vmalloc(a)
+#define SQUASHFS_FREE(a)               vfree(a)
+#else
+#define SQUASHFS_ALLOC(a)              kmalloc(a, GFP_KERNEL)
+#define SQUASHFS_FREE(a)               kfree(a)
+#endif
+#ifdef CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
+#define SQUASHFS_CACHED_FRAGMENTS      CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
+#else
+#define SQUASHFS_CACHED_FRAGMENTS      3
+#endif
+#define SQUASHFS_MAJOR                 3
+#define SQUASHFS_MINOR                 0
+#define SQUASHFS_MAGIC                 0x73717368
+#define SQUASHFS_MAGIC_SWAP            0x68737173
+#define SQUASHFS_START                 0
+
+/* size of metadata (inode and directory) blocks */
+#define SQUASHFS_METADATA_SIZE         8192
+#define SQUASHFS_METADATA_LOG          13
+
+/* default size of data blocks */
+#define SQUASHFS_FILE_SIZE             65536
+#define SQUASHFS_FILE_LOG              16
+
+#define SQUASHFS_FILE_MAX_SIZE         65536
+
+/* Max number of uids and gids */
+#define SQUASHFS_UIDS                  256
+#define SQUASHFS_GUIDS                 255
+
+/* Max length of filename (not 255) */
+#define SQUASHFS_NAME_LEN              256
+
+#define SQUASHFS_INVALID               ((long long) 0xffffffffffff)
+#define SQUASHFS_INVALID_FRAG          ((unsigned int) 0xffffffff)
+#define SQUASHFS_INVALID_BLK           ((long long) -1)
+#define SQUASHFS_USED_BLK              ((long long) -2)
+
+/* Filesystem flags */
+#define SQUASHFS_NOI                   0
+#define SQUASHFS_NOD                   1
+#define SQUASHFS_CHECK                 2
+#define SQUASHFS_NOF                   3
+#define SQUASHFS_NO_FRAG               4
+#define SQUASHFS_ALWAYS_FRAG           5
+#define SQUASHFS_DUPLICATE             6
+
+#define SQUASHFS_BIT(flag, bit)                ((flag >> bit) & 1)
+
+#define SQUASHFS_UNCOMPRESSED_INODES(flags)    SQUASHFS_BIT(flags, \
+                                               SQUASHFS_NOI)
+
+#define SQUASHFS_UNCOMPRESSED_DATA(flags)      SQUASHFS_BIT(flags, \
+                                               SQUASHFS_NOD)
+
+#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
+                                               SQUASHFS_NOF)
+
+#define SQUASHFS_NO_FRAGMENTS(flags)           SQUASHFS_BIT(flags, \
+                                               SQUASHFS_NO_FRAG)
+
+#define SQUASHFS_ALWAYS_FRAGMENTS(flags)       SQUASHFS_BIT(flags, \
+                                               SQUASHFS_ALWAYS_FRAG)
+
+#define SQUASHFS_DUPLICATES(flags)             SQUASHFS_BIT(flags, \
+                                               SQUASHFS_DUPLICATE)
+
+#define SQUASHFS_CHECK_DATA(flags)             SQUASHFS_BIT(flags, \
+                                               SQUASHFS_CHECK)
+
+#define SQUASHFS_MKFLAGS(noi, nod, check_data, nof, no_frag, always_frag, \
+               duplicate_checking)     (noi | (nod << 1) | (check_data << 2) \
+               | (nof << 3) | (no_frag << 4) | (always_frag << 5) | \
+               (duplicate_checking << 6))
+
+/* Max number of types and file types */
+#define SQUASHFS_DIR_TYPE              1
+#define SQUASHFS_FILE_TYPE             2
+#define SQUASHFS_SYMLINK_TYPE          3
+#define SQUASHFS_BLKDEV_TYPE           4
+#define SQUASHFS_CHRDEV_TYPE           5
+#define SQUASHFS_FIFO_TYPE             6
+#define SQUASHFS_SOCKET_TYPE           7
+#define SQUASHFS_LDIR_TYPE             8
+#define SQUASHFS_LREG_TYPE             9
+
+/* 1.0 filesystem type definitions */
+#define SQUASHFS_TYPES                 5
+#define SQUASHFS_IPC_TYPE              0
+
+/* Flag whether block is compressed or uncompressed, bit is set if block is
+ * uncompressed */
+#define SQUASHFS_COMPRESSED_BIT                (1 << 15)
+
+#define SQUASHFS_COMPRESSED_SIZE(B)    (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
+               (B) & ~SQUASHFS_COMPRESSED_BIT :  SQUASHFS_COMPRESSED_BIT)
+
+#define SQUASHFS_COMPRESSED(B)         (!((B) & SQUASHFS_COMPRESSED_BIT))
+
+#define SQUASHFS_COMPRESSED_BIT_BLOCK          (1 << 24)
+
+#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B)      (((B) & \
+       ~SQUASHFS_COMPRESSED_BIT_BLOCK) ? (B) & \
+       ~SQUASHFS_COMPRESSED_BIT_BLOCK : SQUASHFS_COMPRESSED_BIT_BLOCK)
+
+#define SQUASHFS_COMPRESSED_BLOCK(B)   (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
+
+/*
+ * Inode number ops.  Inodes consist of a compressed block number, and an
+ * uncompressed  offset within that block
+ */
+#define SQUASHFS_INODE_BLK(a)          ((unsigned int) ((a) >> 16))
+
+#define SQUASHFS_INODE_OFFSET(a)       ((unsigned int) ((a) & 0xffff))
+
+#define SQUASHFS_MKINODE(A, B)         ((squashfs_inode_t)(((squashfs_inode_t) (A)\
+                                       << 16) + (B)))
+
+/* Compute 32 bit VFS inode number from squashfs inode number */
+#define SQUASHFS_MK_VFS_INODE(a, b)    ((unsigned int) (((a) << 8) + \
+                                       ((b) >> 2) + 1))
+/* XXX */
+
+/* Translate between VFS mode and squashfs mode */
+#define SQUASHFS_MODE(a)               ((a) & 0xfff)
+
+/* fragment and fragment table defines */
+#define SQUASHFS_FRAGMENT_BYTES(A)     (A * sizeof(struct squashfs_fragment_entry))
+
+#define SQUASHFS_FRAGMENT_INDEX(A)     (SQUASHFS_FRAGMENT_BYTES(A) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A)      (SQUASHFS_FRAGMENT_BYTES(A) % \
+                                               SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEXES(A)   ((SQUASHFS_FRAGMENT_BYTES(A) + \
+                                       SQUASHFS_METADATA_SIZE - 1) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_BYTES(A)       (SQUASHFS_FRAGMENT_INDEXES(A) *\
+                                               sizeof(long long))
+
+/* cached data constants for filesystem */
+#define SQUASHFS_CACHED_BLKS           8
+
+#define SQUASHFS_MAX_FILE_SIZE_LOG     64
+
+#define SQUASHFS_MAX_FILE_SIZE         ((long long) 1 << \
+                                       (SQUASHFS_MAX_FILE_SIZE_LOG - 2))
+
+#define SQUASHFS_MARKER_BYTE           0xff
+
+/* meta index cache */
+#define SQUASHFS_META_INDEXES  (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
+#define SQUASHFS_META_ENTRIES  31
+#define SQUASHFS_META_NUMBER   8
+#define SQUASHFS_SLOTS         4
+
+struct meta_entry {
+       long long               data_block;
+       unsigned int            index_block;
+       unsigned short          offset;
+       unsigned short          pad;
+};
+
+struct meta_index {
+       unsigned int            inode_number;
+       unsigned int            offset;
+       unsigned short          entries;
+       unsigned short          skip;
+       unsigned short          locked;
+       unsigned short          pad;
+       struct meta_entry       meta_entry[SQUASHFS_META_ENTRIES];
+};
+
+
+/*
+ * definitions for structures on disk
+ */
+
+typedef long long              squashfs_block_t;
+typedef long long              squashfs_inode_t;
+
+struct squashfs_super_block {
+       unsigned int            s_magic;
+       unsigned int            inodes;
+       unsigned int            bytes_used_2;
+       unsigned int            uid_start_2;
+       unsigned int            guid_start_2;
+       unsigned int            inode_table_start_2;
+       unsigned int            directory_table_start_2;
+       unsigned int            s_major:16;
+       unsigned int            s_minor:16;
+       unsigned int            block_size_1:16;
+       unsigned int            block_log:16;
+       unsigned int            flags:8;
+       unsigned int            no_uids:8;
+       unsigned int            no_guids:8;
+       unsigned int            mkfs_time /* time of filesystem creation */;
+       squashfs_inode_t        root_inode;
+       unsigned int            block_size;
+       unsigned int            fragments;
+       unsigned int            fragment_table_start_2;
+       long long               bytes_used;
+       long long               uid_start;
+       long long               guid_start;
+       long long               inode_table_start;
+       long long               directory_table_start;
+       long long               fragment_table_start;
+       long long               unused;
+} __attribute__ ((packed));
+
+struct squashfs_dir_index {
+       unsigned int            index;
+       unsigned int            start_block;
+       unsigned char           size;
+       unsigned char           name[0];
+} __attribute__ ((packed));
+
+#define SQUASHFS_BASE_INODE_HEADER             \
+       unsigned int            inode_type:4;   \
+       unsigned int            mode:12;        \
+       unsigned int            uid:8;          \
+       unsigned int            guid:8;         \
+       unsigned int            mtime;          \
+       unsigned int            inode_number;
+
+struct squashfs_base_inode_header {
+       SQUASHFS_BASE_INODE_HEADER;
+} __attribute__ ((packed));
+
+struct squashfs_ipc_inode_header {
+       SQUASHFS_BASE_INODE_HEADER;
+       unsigned int            nlink;
+} __attribute__ ((packed));
+
+struct squashfs_dev_inode_header {
+       SQUASHFS_BASE_INODE_HEADER;
+       unsigned int            nlink;
+       unsigned short          rdev;
+} __attribute__ ((packed));
+       
+struct squashfs_symlink_inode_header {
+       SQUASHFS_BASE_INODE_HEADER;
+       unsigned int            nlink;
+       unsigned short          symlink_size;
+       char                    symlink[0];
+} __attribute__ ((packed));
+
+struct squashfs_reg_inode_header {
+       SQUASHFS_BASE_INODE_HEADER;
+       squashfs_block_t        start_block;
+       unsigned int            fragment;
+       unsigned int            offset;
+       unsigned int            file_size;
+       unsigned short          block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_lreg_inode_header {
+       SQUASHFS_BASE_INODE_HEADER;
+       unsigned int            nlink;
+       squashfs_block_t        start_block;
+       unsigned int            fragment;
+       unsigned int            offset;
+       long long               file_size;
+       unsigned short          block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_inode_header {
+       SQUASHFS_BASE_INODE_HEADER;
+       unsigned int            nlink;
+       unsigned int            file_size:19;
+       unsigned int            offset:13;
+       unsigned int            start_block;
+       unsigned int            parent_inode;
+} __attribute__  ((packed));
+
+struct squashfs_ldir_inode_header {
+       SQUASHFS_BASE_INODE_HEADER;
+       unsigned int            nlink;
+       unsigned int            file_size:27;
+       unsigned int            offset:13;
+       unsigned int            start_block;
+       unsigned int            i_count:16;
+       unsigned int            parent_inode;
+       struct squashfs_dir_index       index[0];
+} __attribute__  ((packed));
+
+union squashfs_inode_header {
+       struct squashfs_base_inode_header       base;
+       struct squashfs_dev_inode_header        dev;
+       struct squashfs_symlink_inode_header    symlink;
+       struct squashfs_reg_inode_header        reg;
+       struct squashfs_lreg_inode_header       lreg;
+       struct squashfs_dir_inode_header        dir;
+       struct squashfs_ldir_inode_header       ldir;
+       struct squashfs_ipc_inode_header        ipc;
+};
+       
+struct squashfs_dir_entry {
+       unsigned int            offset:13;
+       unsigned int            type:3;
+       unsigned int            size:8;
+       int                     inode_number:16;
+       char                    name[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_header {
+       unsigned int            count:8;
+       unsigned int            start_block;
+       unsigned int            inode_number;
+} __attribute__ ((packed));
+
+struct squashfs_fragment_entry {
+       long long               start_block;
+       unsigned int            size;
+       unsigned int            unused;
+} __attribute__ ((packed));
+
+extern int squashfs_uncompress_block(void *d, int dstlen, void *s, int srclen);
+extern int squashfs_uncompress_init(void);
+extern int squashfs_uncompress_exit(void);
+
+/*
+ * macros to convert each packed bitfield structure from little endian to big
+ * endian and vice versa.  These are needed when creating or using a filesystem
+ * on a machine with different byte ordering to the target architecture.
+ *
+ */
+
+#define SQUASHFS_SWAP_START \
+       int bits;\
+       int b_pos;\
+       unsigned long long val;\
+       unsigned char *s;\
+       unsigned char *d;
+
+#define SQUASHFS_SWAP_SUPER_BLOCK(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block));\
+       SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\
+       SQUASHFS_SWAP((s)->inodes, d, 32, 32);\
+       SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\
+       SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\
+       SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\
+       SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\
+       SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\
+       SQUASHFS_SWAP((s)->s_major, d, 224, 16);\
+       SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\
+       SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\
+       SQUASHFS_SWAP((s)->block_log, d, 272, 16);\
+       SQUASHFS_SWAP((s)->flags, d, 288, 8);\
+       SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\
+       SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\
+       SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\
+       SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\
+       SQUASHFS_SWAP((s)->block_size, d, 408, 32);\
+       SQUASHFS_SWAP((s)->fragments, d, 440, 32);\
+       SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\
+       SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\
+       SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\
+       SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\
+       SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\
+       SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\
+       SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\
+       SQUASHFS_SWAP((s)->unused, d, 888, 64);\
+}
+
+#define SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
+       SQUASHFS_MEMSET(s, d, n);\
+       SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
+       SQUASHFS_SWAP((s)->mode, d, 4, 12);\
+       SQUASHFS_SWAP((s)->uid, d, 16, 8);\
+       SQUASHFS_SWAP((s)->guid, d, 24, 8);\
+       SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
+       SQUASHFS_SWAP((s)->inode_number, d, 64, 32);
+
+#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, n) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE(s, d, n)\
+}
+
+#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+                       sizeof(struct squashfs_ipc_inode_header))\
+       SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+}
+
+#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+                       sizeof(struct squashfs_dev_inode_header)); \
+       SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+       SQUASHFS_SWAP((s)->rdev, d, 128, 16);\
+}
+
+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+                       sizeof(struct squashfs_symlink_inode_header));\
+       SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+       SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\
+}
+
+#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+                       sizeof(struct squashfs_reg_inode_header));\
+       SQUASHFS_SWAP((s)->start_block, d, 96, 64);\
+       SQUASHFS_SWAP((s)->fragment, d, 160, 32);\
+       SQUASHFS_SWAP((s)->offset, d, 192, 32);\
+       SQUASHFS_SWAP((s)->file_size, d, 224, 32);\
+}
+
+#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+                       sizeof(struct squashfs_lreg_inode_header));\
+       SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+       SQUASHFS_SWAP((s)->start_block, d, 128, 64);\
+       SQUASHFS_SWAP((s)->fragment, d, 192, 32);\
+       SQUASHFS_SWAP((s)->offset, d, 224, 32);\
+       SQUASHFS_SWAP((s)->file_size, d, 256, 64);\
+}
+
+#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+                       sizeof(struct squashfs_dir_inode_header));\
+       SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+       SQUASHFS_SWAP((s)->file_size, d, 128, 19);\
+       SQUASHFS_SWAP((s)->offset, d, 147, 13);\
+       SQUASHFS_SWAP((s)->start_block, d, 160, 32);\
+       SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\
+}
+
+#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE(s, d, \
+                       sizeof(struct squashfs_ldir_inode_header));\
+       SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+       SQUASHFS_SWAP((s)->file_size, d, 128, 27);\
+       SQUASHFS_SWAP((s)->offset, d, 155, 13);\
+       SQUASHFS_SWAP((s)->start_block, d, 168, 32);\
+       SQUASHFS_SWAP((s)->i_count, d, 200, 16);\
+       SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\
+}
+
+#define SQUASHFS_SWAP_DIR_INDEX(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index));\
+       SQUASHFS_SWAP((s)->index, d, 0, 32);\
+       SQUASHFS_SWAP((s)->start_block, d, 32, 32);\
+       SQUASHFS_SWAP((s)->size, d, 64, 8);\
+}
+
+#define SQUASHFS_SWAP_DIR_HEADER(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header));\
+       SQUASHFS_SWAP((s)->count, d, 0, 8);\
+       SQUASHFS_SWAP((s)->start_block, d, 8, 32);\
+       SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\
+}
+
+#define SQUASHFS_SWAP_DIR_ENTRY(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry));\
+       SQUASHFS_SWAP((s)->offset, d, 0, 13);\
+       SQUASHFS_SWAP((s)->type, d, 13, 3);\
+       SQUASHFS_SWAP((s)->size, d, 16, 8);\
+       SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\
+}
+
+#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry));\
+       SQUASHFS_SWAP((s)->start_block, d, 0, 64);\
+       SQUASHFS_SWAP((s)->size, d, 64, 32);\
+}
+
+#define SQUASHFS_SWAP_SHORTS(s, d, n) {\
+       int entry;\
+       int bit_position;\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, n * 2);\
+       for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+                       16)\
+               SQUASHFS_SWAP(s[entry], d, bit_position, 16);\
+}
+
+#define SQUASHFS_SWAP_INTS(s, d, n) {\
+       int entry;\
+       int bit_position;\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, n * 4);\
+       for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+                       32)\
+               SQUASHFS_SWAP(s[entry], d, bit_position, 32);\
+}
+
+#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) {\
+       int entry;\
+       int bit_position;\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, n * 8);\
+       for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+                       64)\
+               SQUASHFS_SWAP(s[entry], d, bit_position, 64);\
+}
+
+#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\
+       int entry;\
+       int bit_position;\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, n * bits / 8);\
+       for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+                       bits)\
+               SQUASHFS_SWAP(s[entry], d, bit_position, bits);\
+}
+
+#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
+
+#ifdef CONFIG_SQUASHFS_1_0_COMPATIBILITY
+
+struct squashfs_base_inode_header_1 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:4; /* index into uid table */
+       unsigned int            guid:4; /* index into guid table */
+} __attribute__ ((packed));
+
+struct squashfs_ipc_inode_header_1 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:4; /* index into uid table */
+       unsigned int            guid:4; /* index into guid table */
+       unsigned int            type:4;
+       unsigned int            offset:4;
+} __attribute__ ((packed));
+
+struct squashfs_dev_inode_header_1 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:4; /* index into uid table */
+       unsigned int            guid:4; /* index into guid table */
+       unsigned short          rdev;
+} __attribute__ ((packed));
+       
+struct squashfs_symlink_inode_header_1 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:4; /* index into uid table */
+       unsigned int            guid:4; /* index into guid table */
+       unsigned short          symlink_size;
+       char                    symlink[0];
+} __attribute__ ((packed));
+
+struct squashfs_reg_inode_header_1 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:4; /* index into uid table */
+       unsigned int            guid:4; /* index into guid table */
+       unsigned int            mtime;
+       unsigned int            start_block;
+       unsigned int            file_size:32;
+       unsigned short          block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_inode_header_1 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:4; /* index into uid table */
+       unsigned int            guid:4; /* index into guid table */
+       unsigned int            file_size:19;
+       unsigned int            offset:13;
+       unsigned int            mtime;
+       unsigned int            start_block:24;
+} __attribute__  ((packed));
+
+#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \
+       SQUASHFS_MEMSET(s, d, n);\
+       SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
+       SQUASHFS_SWAP((s)->mode, d, 4, 12);\
+       SQUASHFS_SWAP((s)->uid, d, 16, 4);\
+       SQUASHFS_SWAP((s)->guid, d, 20, 4);
+
+#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\
+}
+
+#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+                       sizeof(struct squashfs_ipc_inode_header_1));\
+       SQUASHFS_SWAP((s)->type, d, 24, 4);\
+       SQUASHFS_SWAP((s)->offset, d, 28, 4);\
+}
+
+#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+                       sizeof(struct squashfs_dev_inode_header_1));\
+       SQUASHFS_SWAP((s)->rdev, d, 24, 16);\
+}
+
+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+                       sizeof(struct squashfs_symlink_inode_header_1));\
+       SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\
+}
+
+#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+                       sizeof(struct squashfs_reg_inode_header_1));\
+       SQUASHFS_SWAP((s)->mtime, d, 24, 32);\
+       SQUASHFS_SWAP((s)->start_block, d, 56, 32);\
+       SQUASHFS_SWAP((s)->file_size, d, 88, 32);\
+}
+
+#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+                       sizeof(struct squashfs_dir_inode_header_1));\
+       SQUASHFS_SWAP((s)->file_size, d, 24, 19);\
+       SQUASHFS_SWAP((s)->offset, d, 43, 13);\
+       SQUASHFS_SWAP((s)->mtime, d, 56, 32);\
+       SQUASHFS_SWAP((s)->start_block, d, 88, 24);\
+}
+
+#endif
+
+#ifdef CONFIG_SQUASHFS_2_0_COMPATIBILITY
+
+struct squashfs_dir_index_2 {
+       unsigned int            index:27;
+       unsigned int            start_block:29;
+       unsigned char           size;
+       unsigned char           name[0];
+} __attribute__ ((packed));
+
+struct squashfs_base_inode_header_2 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:8; /* index into uid table */
+       unsigned int            guid:8; /* index into guid table */
+} __attribute__ ((packed));
+
+struct squashfs_ipc_inode_header_2 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:8; /* index into uid table */
+       unsigned int            guid:8; /* index into guid table */
+} __attribute__ ((packed));
+
+struct squashfs_dev_inode_header_2 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:8; /* index into uid table */
+       unsigned int            guid:8; /* index into guid table */
+       unsigned short          rdev;
+} __attribute__ ((packed));
+       
+struct squashfs_symlink_inode_header_2 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:8; /* index into uid table */
+       unsigned int            guid:8; /* index into guid table */
+       unsigned short          symlink_size;
+       char                    symlink[0];
+} __attribute__ ((packed));
+
+struct squashfs_reg_inode_header_2 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:8; /* index into uid table */
+       unsigned int            guid:8; /* index into guid table */
+       unsigned int            mtime;
+       unsigned int            start_block;
+       unsigned int            fragment;
+       unsigned int            offset;
+       unsigned int            file_size:32;
+       unsigned short          block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_inode_header_2 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:8; /* index into uid table */
+       unsigned int            guid:8; /* index into guid table */
+       unsigned int            file_size:19;
+       unsigned int            offset:13;
+       unsigned int            mtime;
+       unsigned int            start_block:24;
+} __attribute__  ((packed));
+
+struct squashfs_ldir_inode_header_2 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:8; /* index into uid table */
+       unsigned int            guid:8; /* index into guid table */
+       unsigned int            file_size:27;
+       unsigned int            offset:13;
+       unsigned int            mtime;
+       unsigned int            start_block:24;
+       unsigned int            i_count:16;
+       struct squashfs_dir_index_2     index[0];
+} __attribute__  ((packed));
+
+union squashfs_inode_header_2 {
+       struct squashfs_base_inode_header_2     base;
+       struct squashfs_dev_inode_header_2      dev;
+       struct squashfs_symlink_inode_header_2  symlink;
+       struct squashfs_reg_inode_header_2      reg;
+       struct squashfs_dir_inode_header_2      dir;
+       struct squashfs_ldir_inode_header_2     ldir;
+       struct squashfs_ipc_inode_header_2      ipc;
+};
+       
+struct squashfs_dir_header_2 {
+       unsigned int            count:8;
+       unsigned int            start_block:24;
+} __attribute__ ((packed));
+
+struct squashfs_dir_entry_2 {
+       unsigned int            offset:13;
+       unsigned int            type:3;
+       unsigned int            size:8;
+       char                    name[0];
+} __attribute__ ((packed));
+
+struct squashfs_fragment_entry_2 {
+       unsigned int            start_block;
+       unsigned int            size;
+} __attribute__ ((packed));
+
+#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
+       SQUASHFS_MEMSET(s, d, n);\
+       SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
+       SQUASHFS_SWAP((s)->mode, d, 4, 12);\
+       SQUASHFS_SWAP((s)->uid, d, 16, 8);\
+       SQUASHFS_SWAP((s)->guid, d, 24, 8);\
+
+#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
+}
+
+#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \
+       SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2))
+
+#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+                       sizeof(struct squashfs_dev_inode_header_2)); \
+       SQUASHFS_SWAP((s)->rdev, d, 32, 16);\
+}
+
+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+                       sizeof(struct squashfs_symlink_inode_header_2));\
+       SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\
+}
+
+#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+                       sizeof(struct squashfs_reg_inode_header_2));\
+       SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
+       SQUASHFS_SWAP((s)->start_block, d, 64, 32);\
+       SQUASHFS_SWAP((s)->fragment, d, 96, 32);\
+       SQUASHFS_SWAP((s)->offset, d, 128, 32);\
+       SQUASHFS_SWAP((s)->file_size, d, 160, 32);\
+}
+
+#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+                       sizeof(struct squashfs_dir_inode_header_2));\
+       SQUASHFS_SWAP((s)->file_size, d, 32, 19);\
+       SQUASHFS_SWAP((s)->offset, d, 51, 13);\
+       SQUASHFS_SWAP((s)->mtime, d, 64, 32);\
+       SQUASHFS_SWAP((s)->start_block, d, 96, 24);\
+}
+
+#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+                       sizeof(struct squashfs_ldir_inode_header_2));\
+       SQUASHFS_SWAP((s)->file_size, d, 32, 27);\
+       SQUASHFS_SWAP((s)->offset, d, 59, 13);\
+       SQUASHFS_SWAP((s)->mtime, d, 72, 32);\
+       SQUASHFS_SWAP((s)->start_block, d, 104, 24);\
+       SQUASHFS_SWAP((s)->i_count, d, 128, 16);\
+}
+
+#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\
+       SQUASHFS_SWAP((s)->index, d, 0, 27);\
+       SQUASHFS_SWAP((s)->start_block, d, 27, 29);\
+       SQUASHFS_SWAP((s)->size, d, 56, 8);\
+}
+#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\
+       SQUASHFS_SWAP((s)->count, d, 0, 8);\
+       SQUASHFS_SWAP((s)->start_block, d, 8, 24);\
+}
+
+#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\
+       SQUASHFS_SWAP((s)->offset, d, 0, 13);\
+       SQUASHFS_SWAP((s)->type, d, 13, 3);\
+       SQUASHFS_SWAP((s)->size, d, 16, 8);\
+}
+
+#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\
+       SQUASHFS_SWAP((s)->start_block, d, 0, 32);\
+       SQUASHFS_SWAP((s)->size, d, 32, 32);\
+}
+
+#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS(s, d, n)
+
+/* fragment and fragment table defines */
+#define SQUASHFS_FRAGMENT_BYTES_2(A)   (A * sizeof(struct squashfs_fragment_entry_2))
+
+#define SQUASHFS_FRAGMENT_INDEX_2(A)   (SQUASHFS_FRAGMENT_BYTES_2(A) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A)    (SQUASHFS_FRAGMENT_BYTES_2(A) % \
+                                               SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEXES_2(A) ((SQUASHFS_FRAGMENT_BYTES_2(A) + \
+                                       SQUASHFS_METADATA_SIZE - 1) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A)     (SQUASHFS_FRAGMENT_INDEXES_2(A) *\
+                                               sizeof(int))
+
+#endif
+
+#ifdef __KERNEL__
+
+/*
+ * macros used to swap each structure entry, taking into account
+ * bitfields and different bitfield placing conventions on differing
+ * architectures
+ */
+
+#include <asm/byteorder.h>
+
+#ifdef __BIG_ENDIAN
+       /* convert from little endian to big endian */
+#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
+               tbits, b_pos)
+#else
+       /* convert from big endian to little endian */ 
+#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, \
+               tbits, 64 - tbits - b_pos)
+#endif
+
+#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\
+       b_pos = pos % 8;\
+       val = 0;\
+       s = (unsigned char *)p + (pos / 8);\
+       d = ((unsigned char *) &val) + 7;\
+       for(bits = 0; bits < (tbits + b_pos); bits += 8) \
+               *d-- = *s++;\
+       value = (val >> (SHIFT))/* & ((1 << tbits) - 1)*/;\
+}
+
+#define SQUASHFS_MEMSET(s, d, n)       memset(s, 0, n);
+
+#endif
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/include/linux/squashfs_fs_i.h b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/include/linux/squashfs_fs_i.h
new file mode 100644 (file)
index 0000000..1d7720c
--- /dev/null
@@ -0,0 +1,44 @@
+#ifndef SQUASHFS_FS_I
+#define SQUASHFS_FS_I
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * squashfs_fs_i.h
+ */
+
+struct squashfs_inode_info {
+       long long       start_block;
+       unsigned int    offset;
+       union {
+               struct {
+                       long long       fragment_start_block;
+                       unsigned int    fragment_size;
+                       unsigned int    fragment_offset;
+                       long long       block_list_start;
+               } s1;
+               struct {
+                       long long       directory_index_start;
+                       unsigned int    directory_index_offset;
+                       unsigned int    directory_index_count;
+                       unsigned int    parent_inode;
+               } s2;
+       } u;
+};
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/include/linux/squashfs_fs_sb.h b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.4/include/linux/squashfs_fs_sb.h
new file mode 100644 (file)
index 0000000..ba08d7f
--- /dev/null
@@ -0,0 +1,76 @@
+#ifndef SQUASHFS_FS_SB
+#define SQUASHFS_FS_SB
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * squashfs_fs_sb.h
+ */
+
+#include <linux/squashfs_fs.h>
+#include <linux/zlib.h>
+
+struct squashfs_cache {
+       long long       block;
+       int             length;
+       long long       next_index;
+       char            *data;
+};
+
+struct squashfs_fragment_cache {
+       long long       block;
+       int             length;
+       unsigned int    locked;
+       char            *data;
+};
+
+struct squashfs_sb_info {
+       struct squashfs_super_block     sblk;
+       int                     devblksize;
+       int                     devblksize_log2;
+       int                     swap;
+       struct squashfs_cache   *block_cache;
+       struct squashfs_fragment_cache  *fragment;
+       int                     next_cache;
+       int                     next_fragment;
+       int                     next_meta_index;
+       unsigned int            *uid;
+       unsigned int            *guid;
+       long long               *fragment_index;
+       unsigned int            *fragment_index_2;
+       unsigned int            read_size;
+       char                    *read_data;
+       char                    *read_page;
+       struct semaphore        read_data_mutex;
+       struct semaphore        read_page_mutex;
+       struct semaphore        block_cache_mutex;
+       struct semaphore        fragment_mutex;
+       struct semaphore        meta_index_mutex;
+       wait_queue_head_t       waitq;
+       wait_queue_head_t       fragment_wait_queue;
+       struct meta_index       *meta_index;
+       z_stream                stream;
+       struct inode            *(*iget)(struct super_block *s,  squashfs_inode_t
+                               inode);
+       long long               (*read_blocklist)(struct inode *inode, int
+                               index, int readahead_blks, char *block_list,
+                               unsigned short **block_p, unsigned int *bsize);
+       int                     (*read_fragment_index_table)(struct super_block *s);
+};
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/Documentation/filesystems/squashfs.txt b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/Documentation/filesystems/squashfs.txt
new file mode 100644 (file)
index 0000000..3e79e4a
--- /dev/null
@@ -0,0 +1,225 @@
+SQUASHFS 4.0 FILESYSTEM
+=======================
+
+Squashfs is a compressed read-only filesystem for Linux.
+It uses zlib compression to compress 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 1Mbytes (default block size 128K).
+
+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 constrained
+block device/memory systems (e.g. embedded systems) where low overhead is
+needed.
+
+Mailing list: squashfs-devel@lists.sourceforge.net
+Web site: www.squashfs.org
+
+1. FILESYSTEM FEATURES
+----------------------
+
+Squashfs filesystem features versus Cramfs:
+
+                               Squashfs                Cramfs
+
+Max filesystem size:           2^64                    16 MiB
+Max file size:                 ~ 2 TiB                 16 MiB
+Max files:                     unlimited               unlimited
+Max directories:               unlimited               unlimited
+Max entries per directory:     unlimited               unlimited
+Max block size:                        1 MiB                   4 KiB
+Metadata compression:          yes                     no
+Directory indexes:             yes                     no
+Sparse file support:           yes                     no
+Tail-end packing (fragments):  yes                     no
+Exportable (NFS etc.):         yes                     no
+Hard link support:             yes                     no
+"." and ".." in readdir:       yes                     no
+Real inode numbers:            yes                     no
+32-bit uids/gids:              yes                     no
+File creation time:            yes                     no
+Xattr and ACL support:         no                      no
+
+Squashfs compresses data, inodes and directories.  In addition, inode and
+directory data are highly compacted, and packed on byte boundaries.  Each
+compressed inode is on average 8 bytes in length (the exact length varies on
+file type, i.e. regular file, directory, symbolic link, and block/char device
+inodes have different sizes).
+
+2. USING SQUASHFS
+-----------------
+
+As squashfs is a read-only filesystem, the mksquashfs program must be used to
+create populated squashfs filesystems.  This and other squashfs utilities
+can be obtained from http://www.squashfs.org.  Usage instructions can be
+obtained from this site also.
+
+
+3. SQUASHFS FILESYSTEM DESIGN
+-----------------------------
+
+A squashfs filesystem consists of seven parts, packed together on a byte
+alignment:
+
+        ---------------
+       |  superblock   |
+       |---------------|
+       |  datablocks   |
+       |  & fragments  |
+       |---------------|
+       |  inode table  |
+       |---------------|
+       |   directory   |
+       |     table     |
+       |---------------|
+       |   fragment    |
+       |    table      |
+       |---------------|
+       |    export     |
+       |    table      |
+       |---------------|
+       |    uid/gid    |
+       |  lookup table |
+        ---------------
+
+Compressed data blocks are written to the filesystem as files are read from
+the source directory, and checked for duplicates.  Once all file data has been
+written the completed inode, directory, fragment, export and uid/gid lookup
+tables are written.
+
+3.1 Inodes
+----------
+
+Metadata (inodes and directories) are compressed in 8Kbyte blocks.  Each
+compressed block is prefixed by a two byte length, the top bit is set if the
+block is uncompressed.  A block will be uncompressed if the -noI option is set,
+or if the compressed block was larger than the uncompressed block.
+
+Inodes are packed into the metadata blocks, and are not aligned to block
+boundaries, therefore inodes overlap compressed blocks.  Inodes are identified
+by a 48-bit number which encodes the location of the compressed metadata block
+containing the inode, and the byte offset into that block where the inode is
+placed (<block, offset>).
+
+To maximise compression there are different inodes for each file type
+(regular file, directory, device, etc.), the inode contents and length
+varying with the type.
+
+To further maximise compression, two types of regular file inode and
+directory inode are defined: inodes optimised for frequently occurring
+regular files and directories, and extended types where extra
+information has to be stored.
+
+3.2 Directories
+---------------
+
+Like inodes, directories are packed into compressed metadata blocks, stored
+in a directory table.  Directories are accessed using the start address of
+the metablock containing the directory and the offset into the
+decompressed block (<block, offset>).
+
+Directories are organised in a slightly complex way, and are not simply
+a list of file names.  The organisation takes advantage of the
+fact that (in most cases) the inodes of the files will be in the same
+compressed metadata block, and therefore, can share the start block.
+Directories are therefore organised in a two level list, a directory
+header containing the shared start block value, and a sequence of directory
+entries, each of which share the shared start block.  A new directory header
+is written once/if the inode start block changes.  The directory
+header/directory entry list is repeated as many times as necessary.
+
+Directories are sorted, and can contain a directory index to speed up
+file lookup.  Directory indexes store one entry per metablock, each entry
+storing the index/filename mapping to the first directory header
+in each metadata block.  Directories are sorted in alphabetical order,
+and at lookup the index is scanned linearly looking for the first filename
+alphabetically larger than the filename being looked up.  At this point the
+location of the metadata block the filename is in has been found.
+The general idea of the index is ensure only one metadata block needs to be
+decompressed to do a lookup irrespective of the length of the directory.
+This scheme has the advantage that it doesn't require extra memory overhead
+and doesn't require much extra storage on disk.
+
+3.3 File data
+-------------
+
+Regular files consist of a sequence of contiguous compressed blocks, and/or a
+compressed fragment block (tail-end packed block).   The compressed size
+of each datablock is stored in a block list contained within the
+file inode.
+
+To speed up access to datablocks when reading 'large' files (256 Mbytes or
+larger), the code implements an index cache that caches the mapping from
+block index to datablock location on disk.
+
+The index cache allows Squashfs to handle large files (up to 1.75 TiB) while
+retaining a simple and space-efficient block list on disk.  The cache
+is split into slots, caching up to eight 224 GiB files (128 KiB blocks).
+Larger files use multiple slots, with 1.75 TiB files using all 8 slots.
+The index cache is designed to be memory efficient, and by default uses
+16 KiB.
+
+3.4 Fragment lookup table
+-------------------------
+
+Regular files can contain a fragment index which is mapped to a fragment
+location on disk and compressed size using a fragment lookup table.  This
+fragment lookup table is itself stored compressed into metadata blocks.
+A second index table is used to locate these.  This second index table for
+speed of access (and because it is small) is read at mount time and cached
+in memory.
+
+3.5 Uid/gid lookup table
+------------------------
+
+For space efficiency regular files store uid and gid indexes, which are
+converted to 32-bit uids/gids using an id look up table.  This table is
+stored compressed into metadata blocks.  A second index table is used to
+locate these.  This second index table for speed of access (and because it
+is small) is read at mount time and cached in memory.
+
+3.6 Export table
+----------------
+
+To enable Squashfs filesystems to be exportable (via NFS etc.) filesystems
+can optionally (disabled with the -no-exports Mksquashfs option) contain
+an inode number to inode disk location lookup table.  This is required to
+enable Squashfs to map inode numbers passed in filehandles to the inode
+location on disk, which is necessary when the export code reinstantiates
+expired/flushed inodes.
+
+This table is stored compressed into metadata blocks.  A second index table is
+used to locate these.  This second index table for speed of access (and because
+it is small) is read at mount time and cached in memory.
+
+
+4. TODOS AND OUTSTANDING ISSUES
+-------------------------------
+
+4.1 Todo list
+-------------
+
+Implement Xattr and ACL support.  The Squashfs 4.0 filesystem layout has hooks
+for these but the code has not been written.  Once the code has been written
+the existing layout should not require modification.
+
+4.2 Squashfs internal cache
+---------------------------
+
+Blocks in Squashfs are compressed.  To avoid repeatedly decompressing
+recently accessed data Squashfs uses two small metadata and fragment caches.
+
+The cache is not used for file datablocks, these are decompressed and cached in
+the page-cache in the normal way.  The cache is used to temporarily cache
+fragment and metadata blocks which have been read as a result of a metadata
+(i.e. inode or directory) or fragment access.  Because metadata and fragments
+are packed together into blocks (to gain greater compression) the read of a
+particular piece of metadata or fragment will retrieve other metadata/fragments
+which have been packed with it, these because of locality-of-reference may be
+read in the near future. Temporarily caching them ensures they are available
+for near future access without requiring an additional read and decompress.
+
+In the future this internal cache may be replaced with an implementation which
+uses the kernel page cache.  Because the page cache operates on page sized
+units this may introduce additional complexity in terms of locking and
+associated race conditions.
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/Makefile b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/Makefile
new file mode 100644 (file)
index 0000000..8258cf9
--- /dev/null
@@ -0,0 +1,8 @@
+#
+# Makefile for the linux squashfs routines.
+#
+
+obj-$(CONFIG_SQUASHFS) += squashfs.o
+squashfs-y += block.o cache.o dir.o export.o file.o fragment.o id.o inode.o
+squashfs-y += namei.o super.o symlink.o
+#squashfs-y += squashfs2_0.o
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/block.c b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/block.c
new file mode 100644 (file)
index 0000000..c837dfc
--- /dev/null
@@ -0,0 +1,274 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * block.c
+ */
+
+/*
+ * This file implements the low-level routines to read and decompress
+ * datablocks and metadata blocks.
+ */
+
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/string.h>
+#include <linux/buffer_head.h>
+#include <linux/zlib.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+
+/*
+ * Read the metadata block length, this is stored in the first two
+ * bytes of the metadata block.
+ */
+static struct buffer_head *get_block_length(struct super_block *sb,
+                       u64 *cur_index, int *offset, int *length)
+{
+       struct squashfs_sb_info *msblk = sb->s_fs_info;
+       struct buffer_head *bh;
+
+       bh = sb_bread(sb, *cur_index);
+       if (bh == NULL)
+               return NULL;
+
+       if (msblk->devblksize - *offset == 1) {
+               *length = (unsigned char) bh->b_data[*offset];
+               put_bh(bh);
+               bh = sb_bread(sb, ++(*cur_index));
+               if (bh == NULL)
+                       return NULL;
+               *length |= (unsigned char) bh->b_data[0] << 8;
+               *offset = 1;
+       } else {
+               *length = (unsigned char) bh->b_data[*offset] |
+                       (unsigned char) bh->b_data[*offset + 1] << 8;
+               *offset += 2;
+       }
+
+       return bh;
+}
+
+
+/*
+ * Read and decompress a metadata block or datablock.  Length is non-zero
+ * if a datablock is being read (the size is stored elsewhere in the
+ * filesystem), otherwise the length is obtained from the first two bytes of
+ * the metadata block.  A bit in the length field indicates if the block
+ * is stored uncompressed in the filesystem (usually because compression
+ * generated a larger block - this does occasionally happen with zlib).
+ */
+int squashfs_read_data(struct super_block *sb, void **buffer, u64 index,
+                       int length, u64 *next_index, int srclength)
+{
+       struct squashfs_sb_info *msblk = sb->s_fs_info;
+       struct buffer_head **bh;
+       int offset = index & ((1 << msblk->devblksize_log2) - 1);
+       u64 cur_index = index >> msblk->devblksize_log2;
+       int bytes, compressed, b = 0, k = 0, page = 0, avail;
+
+
+       bh = kcalloc((msblk->block_size >> msblk->devblksize_log2) + 1,
+                               sizeof(*bh), GFP_KERNEL);
+       if (bh == NULL)
+               return -ENOMEM;
+
+       if (length) {
+               /*
+                * Datablock.
+                */
+               bytes = -offset;
+               compressed = SQUASHFS_COMPRESSED_BLOCK(length);
+               length = SQUASHFS_COMPRESSED_SIZE_BLOCK(length);
+               if (next_index)
+                       *next_index = index + length;
+
+               TRACE("Block @ 0x%llx, %scompressed size %d, src size %d\n",
+                       index, compressed ? "" : "un", length, srclength);
+
+               if (length < 0 || length > srclength ||
+                               (index + length) > msblk->bytes_used)
+                       goto read_failure;
+
+               for (b = 0; bytes < length; b++, cur_index++) {
+                       bh[b] = sb_getblk(sb, cur_index);
+                       if (bh[b] == NULL)
+                               goto block_release;
+                       bytes += msblk->devblksize;
+               }
+               ll_rw_block(READ, b, bh);
+       } else {
+               /*
+                * Metadata block.
+                */
+               if ((index + 2) > msblk->bytes_used)
+                       goto read_failure;
+
+               bh[0] = get_block_length(sb, &cur_index, &offset, &length);
+               if (bh[0] == NULL)
+                       goto read_failure;
+               b = 1;
+
+               bytes = msblk->devblksize - offset;
+               compressed = SQUASHFS_COMPRESSED(length);
+               length = SQUASHFS_COMPRESSED_SIZE(length);
+               if (next_index)
+                       *next_index = index + length + 2;
+
+               TRACE("Block @ 0x%llx, %scompressed size %d\n", index,
+                               compressed ? "" : "un", length);
+
+               if (length < 0 || length > srclength ||
+                                       (index + length) > msblk->bytes_used)
+                       goto block_release;
+
+               for (; bytes < length; b++) {
+                       bh[b] = sb_getblk(sb, ++cur_index);
+                       if (bh[b] == NULL)
+                               goto block_release;
+                       bytes += msblk->devblksize;
+               }
+               ll_rw_block(READ, b - 1, bh + 1);
+       }
+
+       if (compressed) {
+               int zlib_err = 0, zlib_init = 0;
+
+               /*
+                * Uncompress block.
+                */
+
+               mutex_lock(&msblk->read_data_mutex);
+
+               msblk->stream.avail_out = 0;
+               msblk->stream.avail_in = 0;
+
+               bytes = length;
+               do {
+                       if (msblk->stream.avail_in == 0 && k < b) {
+                               avail = min(bytes, msblk->devblksize - offset);
+                               bytes -= avail;
+                               wait_on_buffer(bh[k]);
+                               if (!buffer_uptodate(bh[k]))
+                                       goto release_mutex;
+
+                               if (avail == 0) {
+                                       offset = 0;
+                                       put_bh(bh[k++]);
+                                       continue;
+                               }
+
+                               msblk->stream.next_in = bh[k]->b_data + offset;
+                               msblk->stream.avail_in = avail;
+                               offset = 0;
+                       }
+
+                       if (msblk->stream.avail_out == 0) {
+                               msblk->stream.next_out = buffer[page++];
+                               msblk->stream.avail_out = PAGE_CACHE_SIZE;
+                       }
+
+                       if (!zlib_init) {
+                               zlib_err = zlib_inflateInit(&msblk->stream);
+                               if (zlib_err != Z_OK) {
+                                       ERROR("zlib_inflateInit returned"
+                                               " unexpected result 0x%x,"
+                                               " srclength %d\n", zlib_err,
+                                               srclength);
+                                       goto release_mutex;
+                               }
+                               zlib_init = 1;
+                       }
+
+                       zlib_err = zlib_inflate(&msblk->stream, Z_NO_FLUSH);
+
+                       if (msblk->stream.avail_in == 0 && k < b)
+                               put_bh(bh[k++]);
+               } while (zlib_err == Z_OK);
+
+               if (zlib_err != Z_STREAM_END) {
+                       ERROR("zlib_inflate returned unexpected result"
+                               " 0x%x, srclength %d, avail_in %d,"
+                               " avail_out %d\n", zlib_err, srclength,
+                               msblk->stream.avail_in,
+                               msblk->stream.avail_out);
+                       goto release_mutex;
+               }
+
+               zlib_err = zlib_inflateEnd(&msblk->stream);
+               if (zlib_err != Z_OK) {
+                       ERROR("zlib_inflateEnd returned unexpected result 0x%x,"
+                               " srclength %d\n", zlib_err, srclength);
+                       goto release_mutex;
+               }
+               length = msblk->stream.total_out;
+               mutex_unlock(&msblk->read_data_mutex);
+       } else {
+               /*
+                * Block is uncompressed.
+                */
+               int i, in, pg_offset = 0;
+
+               for (i = 0; i < b; i++) {
+                       wait_on_buffer(bh[i]);
+                       if (!buffer_uptodate(bh[i]))
+                               goto block_release;
+               }
+
+               for (bytes = length; k < b; k++) {
+                       in = min(bytes, msblk->devblksize - offset);
+                       bytes -= in;
+                       while (in) {
+                               if (pg_offset == PAGE_CACHE_SIZE) {
+                                       page++;
+                                       pg_offset = 0;
+                               }
+                               avail = min_t(int, in, PAGE_CACHE_SIZE -
+                                               pg_offset);
+                               memcpy(buffer[page] + pg_offset,
+                                               bh[k]->b_data + offset, avail);
+                               in -= avail;
+                               pg_offset += avail;
+                               offset += avail;
+                       }
+                       offset = 0;
+                       put_bh(bh[k]);
+               }
+       }
+
+       kfree(bh);
+       return length;
+
+release_mutex:
+       mutex_unlock(&msblk->read_data_mutex);
+
+block_release:
+       for (; k < b; k++)
+               put_bh(bh[k]);
+
+read_failure:
+       ERROR("sb_bread failed reading block 0x%llx\n", cur_index);
+       kfree(bh);
+       return -EIO;
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/cache.c b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/cache.c
new file mode 100644 (file)
index 0000000..f29eda1
--- /dev/null
@@ -0,0 +1,412 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * cache.c
+ */
+
+/*
+ * Blocks in Squashfs are compressed.  To avoid repeatedly decompressing
+ * recently accessed data Squashfs uses two small metadata and fragment caches.
+ *
+ * This file implements a generic cache implementation used for both caches,
+ * plus functions layered ontop of the generic cache implementation to
+ * access the metadata and fragment caches.
+ *
+ * To avoid out of memory and fragmentation isssues with vmalloc the cache
+ * uses sequences of kmalloced PAGE_CACHE_SIZE buffers.
+ *
+ * It should be noted that the cache is not used for file datablocks, these
+ * are decompressed and cached in the page-cache in the normal way.  The
+ * cache is only used to temporarily cache fragment and metadata blocks
+ * which have been read as as a result of a metadata (i.e. inode or
+ * directory) or fragment access.  Because metadata and fragments are packed
+ * together into blocks (to gain greater compression) the read of a particular
+ * piece of metadata or fragment will retrieve other metadata/fragments which
+ * have been packed with it, these because of locality-of-reference may be read
+ * in the near future. Temporarily caching them ensures they are available for
+ * near future access without requiring an additional read and decompress.
+ */
+
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/slab.h>
+#include <linux/vmalloc.h>
+#include <linux/sched.h>
+#include <linux/spinlock.h>
+#include <linux/wait.h>
+#include <linux/zlib.h>
+#include <linux/pagemap.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+
+/*
+ * Look-up block in cache, and increment usage count.  If not in cache, read
+ * and decompress it from disk.
+ */
+struct squashfs_cache_entry *squashfs_cache_get(struct super_block *sb,
+       struct squashfs_cache *cache, u64 block, int length)
+{
+       int i, n;
+       struct squashfs_cache_entry *entry;
+
+       spin_lock(&cache->lock);
+
+       while (1) {
+               for (i = 0; i < cache->entries; i++)
+                       if (cache->entry[i].block == block)
+                               break;
+
+               if (i == cache->entries) {
+                       /*
+                        * Block not in cache, if all cache entries are used
+                        * go to sleep waiting for one to become available.
+                        */
+                       if (cache->unused == 0) {
+                               cache->num_waiters++;
+                               spin_unlock(&cache->lock);
+                               wait_event(cache->wait_queue, cache->unused);
+                               spin_lock(&cache->lock);
+                               cache->num_waiters--;
+                               continue;
+                       }
+
+                       /*
+                        * At least one unused cache entry.  A simple
+                        * round-robin strategy is used to choose the entry to
+                        * be evicted from the cache.
+                        */
+                       i = cache->next_blk;
+                       for (n = 0; n < cache->entries; n++) {
+                               if (cache->entry[i].refcount == 0)
+                                       break;
+                               i = (i + 1) % cache->entries;
+                       }
+
+                       cache->next_blk = (i + 1) % cache->entries;
+                       entry = &cache->entry[i];
+
+                       /*
+                        * Initialise choosen cache entry, and fill it in from
+                        * disk.
+                        */
+                       cache->unused--;
+                       entry->block = block;
+                       entry->refcount = 1;
+                       entry->pending = 1;
+                       entry->num_waiters = 0;
+                       entry->error = 0;
+                       spin_unlock(&cache->lock);
+
+                       entry->length = squashfs_read_data(sb, entry->data,
+                               block, length, &entry->next_index,
+                               cache->block_size);
+
+                       spin_lock(&cache->lock);
+
+                       if (entry->length < 0)
+                               entry->error = entry->length;
+
+                       entry->pending = 0;
+
+                       /*
+                        * While filling this entry one or more other processes
+                        * have looked it up in the cache, and have slept
+                        * waiting for it to become available.
+                        */
+                       if (entry->num_waiters) {
+                               spin_unlock(&cache->lock);
+                               wake_up_all(&entry->wait_queue);
+                       } else
+                               spin_unlock(&cache->lock);
+
+                       goto out;
+               }
+
+               /*
+                * Block already in cache.  Increment refcount so it doesn't
+                * get reused until we're finished with it, if it was
+                * previously unused there's one less cache entry available
+                * for reuse.
+                */
+               entry = &cache->entry[i];
+               if (entry->refcount == 0)
+                       cache->unused--;
+               entry->refcount++;
+
+               /*
+                * If the entry is currently being filled in by another process
+                * go to sleep waiting for it to become available.
+                */
+               if (entry->pending) {
+                       entry->num_waiters++;
+                       spin_unlock(&cache->lock);
+                       wait_event(entry->wait_queue, !entry->pending);
+               } else
+                       spin_unlock(&cache->lock);
+
+               goto out;
+       }
+
+out:
+       TRACE("Got %s %d, start block %lld, refcount %d, error %d\n",
+               cache->name, i, entry->block, entry->refcount, entry->error);
+
+       if (entry->error)
+               ERROR("Unable to read %s cache entry [%llx]\n", cache->name,
+                                                       block);
+       return entry;
+}
+
+
+/*
+ * Release cache entry, once usage count is zero it can be reused.
+ */
+void squashfs_cache_put(struct squashfs_cache_entry *entry)
+{
+       struct squashfs_cache *cache = entry->cache;
+
+       spin_lock(&cache->lock);
+       entry->refcount--;
+       if (entry->refcount == 0) {
+               cache->unused++;
+               /*
+                * If there's any processes waiting for a block to become
+                * available, wake one up.
+                */
+               if (cache->num_waiters) {
+                       spin_unlock(&cache->lock);
+                       wake_up(&cache->wait_queue);
+                       return;
+               }
+       }
+       spin_unlock(&cache->lock);
+}
+
+/*
+ * Delete cache reclaiming all kmalloced buffers.
+ */
+void squashfs_cache_delete(struct squashfs_cache *cache)
+{
+       int i, j;
+
+       if (cache == NULL)
+               return;
+
+       for (i = 0; i < cache->entries; i++) {
+               if (cache->entry[i].data) {
+                       for (j = 0; j < cache->pages; j++)
+                               kfree(cache->entry[i].data[j]);
+                       kfree(cache->entry[i].data);
+               }
+       }
+
+       kfree(cache->entry);
+       kfree(cache);
+}
+
+
+/*
+ * Initialise cache allocating the specified number of entries, each of
+ * size block_size.  To avoid vmalloc fragmentation issues each entry
+ * is allocated as a sequence of kmalloced PAGE_CACHE_SIZE buffers.
+ */
+struct squashfs_cache *squashfs_cache_init(char *name, int entries,
+       int block_size)
+{
+       int i, j;
+       struct squashfs_cache *cache = kzalloc(sizeof(*cache), GFP_KERNEL);
+
+       if (cache == NULL) {
+               ERROR("Failed to allocate %s cache\n", name);
+               return NULL;
+       }
+
+       cache->entry = kcalloc(entries, sizeof(*(cache->entry)), GFP_KERNEL);
+       if (cache->entry == NULL) {
+               ERROR("Failed to allocate %s cache\n", name);
+               goto cleanup;
+       }
+
+       cache->next_blk = 0;
+       cache->unused = entries;
+       cache->entries = entries;
+       cache->block_size = block_size;
+       cache->pages = block_size >> PAGE_CACHE_SHIFT;
+       cache->name = name;
+       cache->num_waiters = 0;
+       spin_lock_init(&cache->lock);
+       init_waitqueue_head(&cache->wait_queue);
+
+       for (i = 0; i < entries; i++) {
+               struct squashfs_cache_entry *entry = &cache->entry[i];
+
+               init_waitqueue_head(&cache->entry[i].wait_queue);
+               entry->cache = cache;
+               entry->block = SQUASHFS_INVALID_BLK;
+               entry->data = kcalloc(cache->pages, sizeof(void *), GFP_KERNEL);
+               if (entry->data == NULL) {
+                       ERROR("Failed to allocate %s cache entry\n", name);
+                       goto cleanup;
+               }
+
+               for (j = 0; j < cache->pages; j++) {
+                       entry->data[j] = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
+                       if (entry->data[j] == NULL) {
+                               ERROR("Failed to allocate %s buffer\n", name);
+                               goto cleanup;
+                       }
+               }
+       }
+
+       return cache;
+
+cleanup:
+       squashfs_cache_delete(cache);
+       return NULL;
+}
+
+
+/*
+ * Copy upto length bytes from cache entry to buffer starting at offset bytes
+ * into the cache entry.  If there's not length bytes then copy the number of
+ * bytes available.  In all cases return the number of bytes copied.
+ */
+int squashfs_copy_data(void *buffer, struct squashfs_cache_entry *entry,
+               int offset, int length)
+{
+       int remaining = length;
+
+       if (length == 0)
+               return 0;
+       else if (buffer == NULL)
+               return min(length, entry->length - offset);
+
+       while (offset < entry->length) {
+               void *buff = entry->data[offset / PAGE_CACHE_SIZE]
+                               + (offset % PAGE_CACHE_SIZE);
+               int bytes = min_t(int, entry->length - offset,
+                               PAGE_CACHE_SIZE - (offset % PAGE_CACHE_SIZE));
+
+               if (bytes >= remaining) {
+                       memcpy(buffer, buff, remaining);
+                       remaining = 0;
+                       break;
+               }
+
+               memcpy(buffer, buff, bytes);
+               buffer += bytes;
+               remaining -= bytes;
+               offset += bytes;
+       }
+
+       return length - remaining;
+}
+
+
+/*
+ * Read length bytes from metadata position <block, offset> (block is the
+ * start of the compressed block on disk, and offset is the offset into
+ * the block once decompressed).  Data is packed into consecutive blocks,
+ * and length bytes may require reading more than one block.
+ */
+int squashfs_read_metadata(struct super_block *sb, void *buffer,
+               u64 *block, int *offset, int length)
+{
+       struct squashfs_sb_info *msblk = sb->s_fs_info;
+       int bytes, copied = length;
+       struct squashfs_cache_entry *entry;
+
+       TRACE("Entered squashfs_read_metadata [%llx:%x]\n", *block, *offset);
+
+       while (length) {
+               entry = squashfs_cache_get(sb, msblk->block_cache, *block, 0);
+               if (entry->error)
+                       return entry->error;
+               else if (*offset >= entry->length)
+                       return -EIO;
+
+               bytes = squashfs_copy_data(buffer, entry, *offset, length);
+               if (buffer)
+                       buffer += bytes;
+               length -= bytes;
+               *offset += bytes;
+
+               if (*offset == entry->length) {
+                       *block = entry->next_index;
+                       *offset = 0;
+               }
+
+               squashfs_cache_put(entry);
+       }
+
+       return copied;
+}
+
+
+/*
+ * Look-up in the fragmment cache the fragment located at <start_block> in the
+ * filesystem.  If necessary read and decompress it from disk.
+ */
+struct squashfs_cache_entry *squashfs_get_fragment(struct super_block *sb,
+                               u64 start_block, int length)
+{
+       struct squashfs_sb_info *msblk = sb->s_fs_info;
+
+       return squashfs_cache_get(sb, msblk->fragment_cache, start_block,
+               length);
+}
+
+
+/*
+ * Read and decompress the datablock located at <start_block> in the
+ * filesystem.  The cache is used here to avoid duplicating locking and
+ * read/decompress code.
+ */
+struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *sb,
+                               u64 start_block, int length)
+{
+       struct squashfs_sb_info *msblk = sb->s_fs_info;
+
+       return squashfs_cache_get(sb, msblk->read_page, start_block, length);
+}
+
+
+/*
+ * Read a filesystem table (uncompressed sequence of bytes) from disk
+ */
+int squashfs_read_table(struct super_block *sb, void *buffer, u64 block,
+       int length)
+{
+       int pages = (length + PAGE_CACHE_SIZE - 1) >> PAGE_CACHE_SHIFT;
+       int i, res;
+       void **data = kcalloc(pages, sizeof(void *), GFP_KERNEL);
+       if (data == NULL)
+               return -ENOMEM;
+
+       for (i = 0; i < pages; i++, buffer += PAGE_CACHE_SIZE)
+               data[i] = buffer;
+       res = squashfs_read_data(sb, data, block, length |
+               SQUASHFS_COMPRESSED_BIT_BLOCK, NULL, length);
+       kfree(data);
+       return res;
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/dir.c b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/dir.c
new file mode 100644 (file)
index 0000000..566b0ea
--- /dev/null
@@ -0,0 +1,235 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * dir.c
+ */
+
+/*
+ * This file implements code to read directories from disk.
+ *
+ * See namei.c for a description of directory organisation on disk.
+ */
+
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/slab.h>
+#include <linux/zlib.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+
+static const unsigned char squashfs_filetype_table[] = {
+       DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_FIFO, DT_SOCK
+};
+
+/*
+ * Lookup offset (f_pos) in the directory index, returning the
+ * metadata block containing it.
+ *
+ * If we get an error reading the index then return the part of the index
+ * (if any) we have managed to read - the index isn't essential, just
+ * quicker.
+ */
+static int get_dir_index_using_offset(struct super_block *sb,
+       u64 *next_block, int *next_offset, u64 index_start, int index_offset,
+       int i_count, u64 f_pos)
+{
+       struct squashfs_sb_info *msblk = sb->s_fs_info;
+       int err, i, index, length = 0;
+       struct squashfs_dir_index dir_index;
+
+       TRACE("Entered get_dir_index_using_offset, i_count %d, f_pos %lld\n",
+                                       i_count, f_pos);
+
+       /*
+        * Translate from external f_pos to the internal f_pos.  This
+        * is offset by 3 because we invent "." and ".." entries which are
+        * not actually stored in the directory.
+        */
+       if (f_pos < 3)
+               return f_pos;
+       f_pos -= 3;
+
+       for (i = 0; i < i_count; i++) {
+               err = squashfs_read_metadata(sb, &dir_index, &index_start,
+                               &index_offset, sizeof(dir_index));
+               if (err < 0)
+                       break;
+
+               index = le32_to_cpu(dir_index.index);
+               if (index > f_pos)
+                       /*
+                        * Found the index we're looking for.
+                        */
+                       break;
+
+               err = squashfs_read_metadata(sb, NULL, &index_start,
+                               &index_offset, le32_to_cpu(dir_index.size) + 1);
+               if (err < 0)
+                       break;
+
+               length = index;
+               *next_block = le32_to_cpu(dir_index.start_block) +
+                                       msblk->directory_table;
+       }
+
+       *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
+
+       /*
+        * Translate back from internal f_pos to external f_pos.
+        */
+       return length + 3;
+}
+
+
+static int squashfs_readdir(struct file *file, void *dirent, filldir_t filldir)
+{
+       struct inode *inode = file->f_dentry->d_inode;
+       struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+       u64 block = squashfs_i(inode)->start + msblk->directory_table;
+       int offset = squashfs_i(inode)->offset, length = 0, dir_count, size,
+                               type, err;
+       unsigned int inode_number;
+       struct squashfs_dir_header dirh;
+       struct squashfs_dir_entry *dire;
+
+       TRACE("Entered squashfs_readdir [%llx:%x]\n", block, offset);
+
+       dire = kmalloc(sizeof(*dire) + SQUASHFS_NAME_LEN + 1, GFP_KERNEL);
+       if (dire == NULL) {
+               ERROR("Failed to allocate squashfs_dir_entry\n");
+               goto finish;
+       }
+
+       /*
+        * Return "." and  ".." entries as the first two filenames in the
+        * directory.  To maximise compression these two entries are not
+        * stored in the directory, and so we invent them here.
+        *
+        * It also means that the external f_pos is offset by 3 from the
+        * on-disk directory f_pos.
+        */
+       while (file->f_pos < 3) {
+               char *name;
+               int i_ino;
+
+               if (file->f_pos == 0) {
+                       name = ".";
+                       size = 1;
+                       i_ino = inode->i_ino;
+               } else {
+                       name = "..";
+                       size = 2;
+                       i_ino = squashfs_i(inode)->parent;
+               }
+
+               TRACE("Calling filldir(%p, %s, %d, %lld, %d, %d)\n",
+                               dirent, name, size, file->f_pos, i_ino,
+                               squashfs_filetype_table[1]);
+
+               if (filldir(dirent, name, size, file->f_pos, i_ino,
+                               squashfs_filetype_table[1]) < 0) {
+                               TRACE("Filldir returned less than 0\n");
+                       goto finish;
+               }
+
+               file->f_pos += size;
+       }
+
+       length = get_dir_index_using_offset(inode->i_sb, &block, &offset,
+                               squashfs_i(inode)->dir_idx_start,
+                               squashfs_i(inode)->dir_idx_offset,
+                               squashfs_i(inode)->dir_idx_cnt,
+                               file->f_pos);
+
+       while (length < i_size_read(inode)) {
+               /*
+                * Read directory header
+                */
+               err = squashfs_read_metadata(inode->i_sb, &dirh, &block,
+                                       &offset, sizeof(dirh));
+               if (err < 0)
+                       goto failed_read;
+
+               length += sizeof(dirh);
+
+               dir_count = le32_to_cpu(dirh.count) + 1;
+               while (dir_count--) {
+                       /*
+                        * Read directory entry.
+                        */
+                       err = squashfs_read_metadata(inode->i_sb, dire, &block,
+                                       &offset, sizeof(*dire));
+                       if (err < 0)
+                               goto failed_read;
+
+                       size = le16_to_cpu(dire->size) + 1;
+
+                       err = squashfs_read_metadata(inode->i_sb, dire->name,
+                                       &block, &offset, size);
+                       if (err < 0)
+                               goto failed_read;
+
+                       length += sizeof(*dire) + size;
+
+                       if (file->f_pos >= length)
+                               continue;
+
+                       dire->name[size] = '\0';
+                       inode_number = le32_to_cpu(dirh.inode_number) +
+                               ((short) le16_to_cpu(dire->inode_number));
+                       type = le16_to_cpu(dire->type);
+
+                       TRACE("Calling filldir(%p, %s, %d, %lld, %x:%x, %d, %d)"
+                                       "\n", dirent, dire->name, size,
+                                       file->f_pos,
+                                       le32_to_cpu(dirh.start_block),
+                                       le16_to_cpu(dire->offset),
+                                       inode_number,
+                                       squashfs_filetype_table[type]);
+
+                       if (filldir(dirent, dire->name, size, file->f_pos,
+                                       inode_number,
+                                       squashfs_filetype_table[type]) < 0) {
+                               TRACE("Filldir returned less than 0\n");
+                               goto finish;
+                       }
+
+                       file->f_pos = length;
+               }
+       }
+
+finish:
+       kfree(dire);
+       return 0;
+
+failed_read:
+       ERROR("Unable to read directory block [%llx:%x]\n", block, offset);
+       kfree(dire);
+       return 0;
+}
+
+
+const struct file_operations squashfs_dir_ops = {
+       .read = generic_read_dir,
+       .readdir = squashfs_readdir
+};
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/export.c b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/export.c
new file mode 100644 (file)
index 0000000..69e971d
--- /dev/null
@@ -0,0 +1,155 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * export.c
+ */
+
+/*
+ * This file implements code to make Squashfs filesystems exportable (NFS etc.)
+ *
+ * The export code uses an inode lookup table to map inode numbers passed in
+ * filehandles to an inode location on disk.  This table is stored compressed
+ * into metadata blocks.  A second index table is used to locate these.  This
+ * second index table for speed of access (and because it is small) is read at
+ * mount time and cached in memory.
+ *
+ * The inode lookup table is used only by the export code, inode disk
+ * locations are directly encoded in directories, enabling direct access
+ * without an intermediate lookup for all operations except the export ops.
+ */
+
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/dcache.h>
+#include <linux/exportfs.h>
+#include <linux/zlib.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+
+/*
+ * Look-up inode number (ino) in table, returning the inode location.
+ */
+static long long squashfs_inode_lookup(struct super_block *sb, int ino_num)
+{
+       struct squashfs_sb_info *msblk = sb->s_fs_info;
+       int blk = SQUASHFS_LOOKUP_BLOCK(ino_num - 1);
+       int offset = SQUASHFS_LOOKUP_BLOCK_OFFSET(ino_num - 1);
+       u64 start = le64_to_cpu(msblk->inode_lookup_table[blk]);
+       __le64 ino;
+       int err;
+
+       TRACE("Entered squashfs_inode_lookup, inode_number = %d\n", ino_num);
+
+       err = squashfs_read_metadata(sb, &ino, &start, &offset, sizeof(ino));
+       if (err < 0)
+               return err;
+
+       TRACE("squashfs_inode_lookup, inode = 0x%llx\n",
+               (u64) le64_to_cpu(ino));
+
+       return le64_to_cpu(ino);
+}
+
+
+static struct dentry *squashfs_export_iget(struct super_block *sb,
+       unsigned int ino_num)
+{
+       long long ino;
+       struct dentry *dentry = ERR_PTR(-ENOENT);
+
+       TRACE("Entered squashfs_export_iget\n");
+
+       ino = squashfs_inode_lookup(sb, ino_num);
+       if (ino >= 0)
+               dentry = d_obtain_alias(squashfs_iget(sb, ino, ino_num));
+
+       return dentry;
+}
+
+
+static struct dentry *squashfs_fh_to_dentry(struct super_block *sb,
+               struct fid *fid, int fh_len, int fh_type)
+{
+       if ((fh_type != FILEID_INO32_GEN && fh_type != FILEID_INO32_GEN_PARENT)
+                       || fh_len < 2)
+               return NULL;
+
+       return squashfs_export_iget(sb, fid->i32.ino);
+}
+
+
+static struct dentry *squashfs_fh_to_parent(struct super_block *sb,
+               struct fid *fid, int fh_len, int fh_type)
+{
+       if (fh_type != FILEID_INO32_GEN_PARENT || fh_len < 4)
+               return NULL;
+
+       return squashfs_export_iget(sb, fid->i32.parent_ino);
+}
+
+
+static struct dentry *squashfs_get_parent(struct dentry *child)
+{
+       struct inode *inode = child->d_inode;
+       unsigned int parent_ino = squashfs_i(inode)->parent;
+
+       return squashfs_export_iget(inode->i_sb, parent_ino);
+}
+
+
+/*
+ * Read uncompressed inode lookup table indexes off disk into memory
+ */
+__le64 *squashfs_read_inode_lookup_table(struct super_block *sb,
+               u64 lookup_table_start, unsigned int inodes)
+{
+       unsigned int length = SQUASHFS_LOOKUP_BLOCK_BYTES(inodes);
+       __le64 *inode_lookup_table;
+       int err;
+
+       TRACE("In read_inode_lookup_table, length %d\n", length);
+
+       /* Allocate inode lookup table indexes */
+       inode_lookup_table = kmalloc(length, GFP_KERNEL);
+       if (inode_lookup_table == NULL) {
+               ERROR("Failed to allocate inode lookup table\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       err = squashfs_read_table(sb, inode_lookup_table, lookup_table_start,
+                       length);
+       if (err < 0) {
+               ERROR("unable to read inode lookup table\n");
+               kfree(inode_lookup_table);
+               return ERR_PTR(err);
+       }
+
+       return inode_lookup_table;
+}
+
+
+const struct export_operations squashfs_export_ops = {
+       .fh_to_dentry = squashfs_fh_to_dentry,
+       .fh_to_parent = squashfs_fh_to_parent,
+       .get_parent = squashfs_get_parent
+};
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/file.c b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/file.c
new file mode 100644 (file)
index 0000000..717767d
--- /dev/null
@@ -0,0 +1,502 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * file.c
+ */
+
+/*
+ * This file contains code for handling regular files.  A regular file
+ * consists of a sequence of contiguous compressed blocks, and/or a
+ * compressed fragment block (tail-end packed block).   The compressed size
+ * of each datablock is stored in a block list contained within the
+ * file inode (itself stored in one or more compressed metadata blocks).
+ *
+ * To speed up access to datablocks when reading 'large' files (256 Mbytes or
+ * larger), the code implements an index cache that caches the mapping from
+ * block index to datablock location on disk.
+ *
+ * The index cache allows Squashfs to handle large files (up to 1.75 TiB) while
+ * retaining a simple and space-efficient block list on disk.  The cache
+ * is split into slots, caching up to eight 224 GiB files (128 KiB blocks).
+ * Larger files use multiple slots, with 1.75 TiB files using all 8 slots.
+ * The index cache is designed to be memory efficient, and by default uses
+ * 16 KiB.
+ */
+
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/pagemap.h>
+#include <linux/mutex.h>
+#include <linux/zlib.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+
+/*
+ * Locate cache slot in range [offset, index] for specified inode.  If
+ * there's more than one return the slot closest to index.
+ */
+static struct meta_index *locate_meta_index(struct inode *inode, int offset,
+                               int index)
+{
+       struct meta_index *meta = NULL;
+       struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+       int i;
+
+       mutex_lock(&msblk->meta_index_mutex);
+
+       TRACE("locate_meta_index: index %d, offset %d\n", index, offset);
+
+       if (msblk->meta_index == NULL)
+               goto not_allocated;
+
+       for (i = 0; i < SQUASHFS_META_SLOTS; i++) {
+               if (msblk->meta_index[i].inode_number == inode->i_ino &&
+                               msblk->meta_index[i].offset >= offset &&
+                               msblk->meta_index[i].offset <= index &&
+                               msblk->meta_index[i].locked == 0) {
+                       TRACE("locate_meta_index: entry %d, offset %d\n", i,
+                                       msblk->meta_index[i].offset);
+                       meta = &msblk->meta_index[i];
+                       offset = meta->offset;
+               }
+       }
+
+       if (meta)
+               meta->locked = 1;
+
+not_allocated:
+       mutex_unlock(&msblk->meta_index_mutex);
+
+       return meta;
+}
+
+
+/*
+ * Find and initialise an empty cache slot for index offset.
+ */
+static struct meta_index *empty_meta_index(struct inode *inode, int offset,
+                               int skip)
+{
+       struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+       struct meta_index *meta = NULL;
+       int i;
+
+       mutex_lock(&msblk->meta_index_mutex);
+
+       TRACE("empty_meta_index: offset %d, skip %d\n", offset, skip);
+
+       if (msblk->meta_index == NULL) {
+               /*
+                * First time cache index has been used, allocate and
+                * initialise.  The cache index could be allocated at
+                * mount time but doing it here means it is allocated only
+                * if a 'large' file is read.
+                */
+               msblk->meta_index = kcalloc(SQUASHFS_META_SLOTS,
+                       sizeof(*(msblk->meta_index)), GFP_KERNEL);
+               if (msblk->meta_index == NULL) {
+                       ERROR("Failed to allocate meta_index\n");
+                       goto failed;
+               }
+               for (i = 0; i < SQUASHFS_META_SLOTS; i++) {
+                       msblk->meta_index[i].inode_number = 0;
+                       msblk->meta_index[i].locked = 0;
+               }
+               msblk->next_meta_index = 0;
+       }
+
+       for (i = SQUASHFS_META_SLOTS; i &&
+                       msblk->meta_index[msblk->next_meta_index].locked; i--)
+               msblk->next_meta_index = (msblk->next_meta_index + 1) %
+                       SQUASHFS_META_SLOTS;
+
+       if (i == 0) {
+               TRACE("empty_meta_index: failed!\n");
+               goto failed;
+       }
+
+       TRACE("empty_meta_index: returned meta entry %d, %p\n",
+                       msblk->next_meta_index,
+                       &msblk->meta_index[msblk->next_meta_index]);
+
+       meta = &msblk->meta_index[msblk->next_meta_index];
+       msblk->next_meta_index = (msblk->next_meta_index + 1) %
+                       SQUASHFS_META_SLOTS;
+
+       meta->inode_number = inode->i_ino;
+       meta->offset = offset;
+       meta->skip = skip;
+       meta->entries = 0;
+       meta->locked = 1;
+
+failed:
+       mutex_unlock(&msblk->meta_index_mutex);
+       return meta;
+}
+
+
+static void release_meta_index(struct inode *inode, struct meta_index *meta)
+{
+       struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+       mutex_lock(&msblk->meta_index_mutex);
+       meta->locked = 0;
+       mutex_unlock(&msblk->meta_index_mutex);
+}
+
+
+/*
+ * Read the next n blocks from the block list, starting from
+ * metadata block <start_block, offset>.
+ */
+static long long read_indexes(struct super_block *sb, int n,
+                               u64 *start_block, int *offset)
+{
+       int err, i;
+       long long block = 0;
+       __le32 *blist = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL);
+
+       if (blist == NULL) {
+               ERROR("read_indexes: Failed to allocate block_list\n");
+               return -ENOMEM;
+       }
+
+       while (n) {
+               int blocks = min_t(int, n, PAGE_CACHE_SIZE >> 2);
+
+               err = squashfs_read_metadata(sb, blist, start_block,
+                               offset, blocks << 2);
+               if (err < 0) {
+                       ERROR("read_indexes: reading block [%llx:%x]\n",
+                               *start_block, *offset);
+                       goto failure;
+               }
+
+               for (i = 0; i < blocks; i++) {
+                       int size = le32_to_cpu(blist[i]);
+                       block += SQUASHFS_COMPRESSED_SIZE_BLOCK(size);
+               }
+               n -= blocks;
+       }
+
+       kfree(blist);
+       return block;
+
+failure:
+       kfree(blist);
+       return err;
+}
+
+
+/*
+ * Each cache index slot has SQUASHFS_META_ENTRIES, each of which
+ * can cache one index -> datablock/blocklist-block mapping.  We wish
+ * to distribute these over the length of the file, entry[0] maps index x,
+ * entry[1] maps index x + skip, entry[2] maps index x + 2 * skip, and so on.
+ * The larger the file, the greater the skip factor.  The skip factor is
+ * limited to the size of the metadata cache (SQUASHFS_CACHED_BLKS) to ensure
+ * the number of metadata blocks that need to be read fits into the cache.
+ * If the skip factor is limited in this way then the file will use multiple
+ * slots.
+ */
+static inline int calculate_skip(int blocks)
+{
+       int skip = blocks / ((SQUASHFS_META_ENTRIES + 1)
+                * SQUASHFS_META_INDEXES);
+       return min(SQUASHFS_CACHED_BLKS - 1, skip + 1);
+}
+
+
+/*
+ * Search and grow the index cache for the specified inode, returning the
+ * on-disk locations of the datablock and block list metadata block
+ * <index_block, index_offset> for index (scaled to nearest cache index).
+ */
+static int fill_meta_index(struct inode *inode, int index,
+               u64 *index_block, int *index_offset, u64 *data_block)
+{
+       struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+       int skip = calculate_skip(i_size_read(inode) >> msblk->block_log);
+       int offset = 0;
+       struct meta_index *meta;
+       struct meta_entry *meta_entry;
+       u64 cur_index_block = squashfs_i(inode)->block_list_start;
+       int cur_offset = squashfs_i(inode)->offset;
+       u64 cur_data_block = squashfs_i(inode)->start;
+       int err, i;
+
+       /*
+        * Scale index to cache index (cache slot entry)
+        */
+       index /= SQUASHFS_META_INDEXES * skip;
+
+       while (offset < index) {
+               meta = locate_meta_index(inode, offset + 1, index);
+
+               if (meta == NULL) {
+                       meta = empty_meta_index(inode, offset + 1, skip);
+                       if (meta == NULL)
+                               goto all_done;
+               } else {
+                       offset = index < meta->offset + meta->entries ? index :
+                               meta->offset + meta->entries - 1;
+                       meta_entry = &meta->meta_entry[offset - meta->offset];
+                       cur_index_block = meta_entry->index_block +
+                               msblk->inode_table;
+                       cur_offset = meta_entry->offset;
+                       cur_data_block = meta_entry->data_block;
+                       TRACE("get_meta_index: offset %d, meta->offset %d, "
+                               "meta->entries %d\n", offset, meta->offset,
+                               meta->entries);
+                       TRACE("get_meta_index: index_block 0x%llx, offset 0x%x"
+                               " data_block 0x%llx\n", cur_index_block,
+                               cur_offset, cur_data_block);
+               }
+
+               /*
+                * If necessary grow cache slot by reading block list.  Cache
+                * slot is extended up to index or to the end of the slot, in
+                * which case further slots will be used.
+                */
+               for (i = meta->offset + meta->entries; i <= index &&
+                               i < meta->offset + SQUASHFS_META_ENTRIES; i++) {
+                       int blocks = skip * SQUASHFS_META_INDEXES;
+                       long long res = read_indexes(inode->i_sb, blocks,
+                                       &cur_index_block, &cur_offset);
+
+                       if (res < 0) {
+                               if (meta->entries == 0)
+                                       /*
+                                        * Don't leave an empty slot on read
+                                        * error allocated to this inode...
+                                        */
+                                       meta->inode_number = 0;
+                               err = res;
+                               goto failed;
+                       }
+
+                       cur_data_block += res;
+                       meta_entry = &meta->meta_entry[i - meta->offset];
+                       meta_entry->index_block = cur_index_block -
+                               msblk->inode_table;
+                       meta_entry->offset = cur_offset;
+                       meta_entry->data_block = cur_data_block;
+                       meta->entries++;
+                       offset++;
+               }
+
+               TRACE("get_meta_index: meta->offset %d, meta->entries %d\n",
+                               meta->offset, meta->entries);
+
+               release_meta_index(inode, meta);
+       }
+
+all_done:
+       *index_block = cur_index_block;
+       *index_offset = cur_offset;
+       *data_block = cur_data_block;
+
+       /*
+        * Scale cache index (cache slot entry) to index
+        */
+       return offset * SQUASHFS_META_INDEXES * skip;
+
+failed:
+       release_meta_index(inode, meta);
+       return err;
+}
+
+
+/*
+ * Get the on-disk location and compressed size of the datablock
+ * specified by index.  Fill_meta_index() does most of the work.
+ */
+static int read_blocklist(struct inode *inode, int index, u64 *block)
+{
+       u64 start;
+       long long blks;
+       int offset;
+       __le32 size;
+       int res = fill_meta_index(inode, index, &start, &offset, block);
+
+       TRACE("read_blocklist: res %d, index %d, start 0x%llx, offset"
+                      " 0x%x, block 0x%llx\n", res, index, start, offset,
+                       *block);
+
+       if (res < 0)
+               return res;
+
+       /*
+        * res contains the index of the mapping returned by fill_meta_index(),
+        * this will likely be less than the desired index (because the
+        * meta_index cache works at a higher granularity).  Read any
+        * extra block indexes needed.
+        */
+       if (res < index) {
+               blks = read_indexes(inode->i_sb, index - res, &start, &offset);
+               if (blks < 0)
+                       return (int) blks;
+               *block += blks;
+       }
+
+       /*
+        * Read length of block specified by index.
+        */
+       res = squashfs_read_metadata(inode->i_sb, &size, &start, &offset,
+                       sizeof(size));
+       if (res < 0)
+               return res;
+       return le32_to_cpu(size);
+}
+
+
+static int squashfs_readpage(struct file *file, struct page *page)
+{
+       struct inode *inode = page->mapping->host;
+       struct squashfs_sb_info *msblk = inode->i_sb->s_fs_info;
+       int bytes, i, offset = 0, sparse = 0;
+       struct squashfs_cache_entry *buffer = NULL;
+       void *pageaddr;
+
+       int mask = (1 << (msblk->block_log - PAGE_CACHE_SHIFT)) - 1;
+       int index = page->index >> (msblk->block_log - PAGE_CACHE_SHIFT);
+       int start_index = page->index & ~mask;
+       int end_index = start_index | mask;
+       int file_end = i_size_read(inode) >> msblk->block_log;
+
+       TRACE("Entered squashfs_readpage, page index %lx, start block %llx\n",
+                               page->index, squashfs_i(inode)->start);
+
+       if (page->index >= ((i_size_read(inode) + PAGE_CACHE_SIZE - 1) >>
+                                       PAGE_CACHE_SHIFT))
+               goto out;
+
+       if (index < file_end || squashfs_i(inode)->fragment_block ==
+                                       SQUASHFS_INVALID_BLK) {
+               /*
+                * Reading a datablock from disk.  Need to read block list
+                * to get location and block size.
+                */
+               u64 block = 0;
+               int bsize = read_blocklist(inode, index, &block);
+               if (bsize < 0)
+                       goto error_out;
+
+               if (bsize == 0) { /* hole */
+                       bytes = index == file_end ?
+                               (i_size_read(inode) & (msblk->block_size - 1)) :
+                                msblk->block_size;
+                       sparse = 1;
+               } else {
+                       /*
+                        * Read and decompress datablock.
+                        */
+                       buffer = squashfs_get_datablock(inode->i_sb,
+                                                               block, bsize);
+                       if (buffer->error) {
+                               ERROR("Unable to read page, block %llx, size %x"
+                                       "\n", block, bsize);
+                               squashfs_cache_put(buffer);
+                               goto error_out;
+                       }
+                       bytes = buffer->length;
+               }
+       } else {
+               /*
+                * Datablock is stored inside a fragment (tail-end packed
+                * block).
+                */
+               buffer = squashfs_get_fragment(inode->i_sb,
+                               squashfs_i(inode)->fragment_block,
+                               squashfs_i(inode)->fragment_size);
+
+               if (buffer->error) {
+                       ERROR("Unable to read page, block %llx, size %x\n",
+                               squashfs_i(inode)->fragment_block,
+                               squashfs_i(inode)->fragment_size);
+                       squashfs_cache_put(buffer);
+                       goto error_out;
+               }
+               bytes = i_size_read(inode) & (msblk->block_size - 1);
+               offset = squashfs_i(inode)->fragment_offset;
+       }
+
+       /*
+        * Loop copying datablock into pages.  As the datablock likely covers
+        * many PAGE_CACHE_SIZE pages (default block size is 128 KiB) explicitly
+        * grab the pages from the page cache, except for the page that we've
+        * been called to fill.
+        */
+       for (i = start_index; i <= end_index && bytes > 0; i++,
+                       bytes -= PAGE_CACHE_SIZE, offset += PAGE_CACHE_SIZE) {
+               struct page *push_page;
+               int avail = sparse ? 0 : min_t(int, bytes, PAGE_CACHE_SIZE);
+
+               TRACE("bytes %d, i %d, available_bytes %d\n", bytes, i, avail);
+
+               push_page = (i == page->index) ? page :
+                       grab_cache_page_nowait(page->mapping, i);
+
+               if (!push_page)
+                       continue;
+
+               if (PageUptodate(push_page))
+                       goto skip_page;
+
+               pageaddr = kmap_atomic(push_page, KM_USER0);
+               squashfs_copy_data(pageaddr, buffer, offset, avail);
+               memset(pageaddr + avail, 0, PAGE_CACHE_SIZE - avail);
+               kunmap_atomic(pageaddr, KM_USER0);
+               flush_dcache_page(push_page);
+               SetPageUptodate(push_page);
+skip_page:
+               unlock_page(push_page);
+               if (i != page->index)
+                       page_cache_release(push_page);
+       }
+
+       if (!sparse)
+               squashfs_cache_put(buffer);
+
+       return 0;
+
+error_out:
+       SetPageError(page);
+out:
+       pageaddr = kmap_atomic(page, KM_USER0);
+       memset(pageaddr, 0, PAGE_CACHE_SIZE);
+       kunmap_atomic(pageaddr, KM_USER0);
+       flush_dcache_page(page);
+       if (!PageError(page))
+               SetPageUptodate(page);
+       unlock_page(page);
+
+       return 0;
+}
+
+
+const struct address_space_operations squashfs_aops = {
+       .readpage = squashfs_readpage
+};
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/fragment.c b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/fragment.c
new file mode 100644 (file)
index 0000000..b5a2c15
--- /dev/null
@@ -0,0 +1,98 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * fragment.c
+ */
+
+/*
+ * This file implements code to handle compressed fragments (tail-end packed
+ * datablocks).
+ *
+ * Regular files contain a fragment index which is mapped to a fragment
+ * location on disk and compressed size using a fragment lookup table.
+ * Like everything in Squashfs this fragment lookup table is itself stored
+ * compressed into metadata blocks.  A second index table is used to locate
+ * these.  This second index table for speed of access (and because it
+ * is small) is read at mount time and cached in memory.
+ */
+
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/slab.h>
+#include <linux/zlib.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+
+/*
+ * Look-up fragment using the fragment index table.  Return the on disk
+ * location of the fragment and its compressed size
+ */
+int squashfs_frag_lookup(struct super_block *sb, unsigned int fragment,
+                               u64 *fragment_block)
+{
+       struct squashfs_sb_info *msblk = sb->s_fs_info;
+       int block = SQUASHFS_FRAGMENT_INDEX(fragment);
+       int offset = SQUASHFS_FRAGMENT_INDEX_OFFSET(fragment);
+       u64 start_block = le64_to_cpu(msblk->fragment_index[block]);
+       struct squashfs_fragment_entry fragment_entry;
+       int size;
+
+       size = squashfs_read_metadata(sb, &fragment_entry, &start_block,
+                                       &offset, sizeof(fragment_entry));
+       if (size < 0)
+               return size;
+
+       *fragment_block = le64_to_cpu(fragment_entry.start_block);
+       size = le32_to_cpu(fragment_entry.size);
+
+       return size;
+}
+
+
+/*
+ * Read the uncompressed fragment lookup table indexes off disk into memory
+ */
+__le64 *squashfs_read_fragment_index_table(struct super_block *sb,
+       u64 fragment_table_start, unsigned int fragments)
+{
+       unsigned int length = SQUASHFS_FRAGMENT_INDEX_BYTES(fragments);
+       __le64 *fragment_index;
+       int err;
+
+       /* Allocate fragment lookup table indexes */
+       fragment_index = kmalloc(length, GFP_KERNEL);
+       if (fragment_index == NULL) {
+               ERROR("Failed to allocate fragment index table\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       err = squashfs_read_table(sb, fragment_index, fragment_table_start,
+                       length);
+       if (err < 0) {
+               ERROR("unable to read fragment index table\n");
+               kfree(fragment_index);
+               return ERR_PTR(err);
+       }
+
+       return fragment_index;
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/id.c b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/id.c
new file mode 100644 (file)
index 0000000..3795b83
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * id.c
+ */
+
+/*
+ * This file implements code to handle uids and gids.
+ *
+ * For space efficiency regular files store uid and gid indexes, which are
+ * converted to 32-bit uids/gids using an id look up table.  This table is
+ * stored compressed into metadata blocks.  A second index table is used to
+ * locate these.  This second index table for speed of access (and because it
+ * is small) is read at mount time and cached in memory.
+ */
+
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/slab.h>
+#include <linux/zlib.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+
+/*
+ * Map uid/gid index into real 32-bit uid/gid using the id look up table
+ */
+int squashfs_get_id(struct super_block *sb, unsigned int index,
+                                       unsigned int *id)
+{
+       struct squashfs_sb_info *msblk = sb->s_fs_info;
+       int block = SQUASHFS_ID_BLOCK(index);
+       int offset = SQUASHFS_ID_BLOCK_OFFSET(index);
+       u64 start_block = le64_to_cpu(msblk->id_table[block]);
+       __le32 disk_id;
+       int err;
+
+       err = squashfs_read_metadata(sb, &disk_id, &start_block, &offset,
+                                                       sizeof(disk_id));
+       if (err < 0)
+               return err;
+
+       *id = le32_to_cpu(disk_id);
+       return 0;
+}
+
+
+/*
+ * Read uncompressed id lookup table indexes from disk into memory
+ */
+__le64 *squashfs_read_id_index_table(struct super_block *sb,
+                       u64 id_table_start, unsigned short no_ids)
+{
+       unsigned int length = SQUASHFS_ID_BLOCK_BYTES(no_ids);
+       __le64 *id_table;
+       int err;
+
+       TRACE("In read_id_index_table, length %d\n", length);
+
+       /* Allocate id lookup table indexes */
+       id_table = kmalloc(length, GFP_KERNEL);
+       if (id_table == NULL) {
+               ERROR("Failed to allocate id index table\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       err = squashfs_read_table(sb, id_table, id_table_start, length);
+       if (err < 0) {
+               ERROR("unable to read id index table\n");
+               kfree(id_table);
+               return ERR_PTR(err);
+       }
+
+       return id_table;
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/inode.c b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/inode.c
new file mode 100644 (file)
index 0000000..7a63398
--- /dev/null
@@ -0,0 +1,346 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * inode.c
+ */
+
+/*
+ * This file implements code to create and read inodes from disk.
+ *
+ * Inodes in Squashfs are identified by a 48-bit inode which encodes the
+ * location of the compressed metadata block containing the inode, and the byte
+ * offset into that block where the inode is placed (<block, offset>).
+ *
+ * To maximise compression there are different inodes for each file type
+ * (regular file, directory, device, etc.), the inode contents and length
+ * varying with the type.
+ *
+ * To further maximise compression, two types of regular file inode and
+ * directory inode are defined: inodes optimised for frequently occurring
+ * regular files and directories, and extended types where extra
+ * information has to be stored.
+ */
+
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/zlib.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+
+/*
+ * Initialise VFS inode with the base inode information common to all
+ * Squashfs inode types.  Sqsh_ino contains the unswapped base inode
+ * off disk.
+ */
+static int squashfs_new_inode(struct super_block *sb, struct inode *inode,
+                               struct squashfs_base_inode *sqsh_ino)
+{
+       int err;
+
+       err = squashfs_get_id(sb, le16_to_cpu(sqsh_ino->uid), &inode->i_uid);
+       if (err)
+               return err;
+
+       err = squashfs_get_id(sb, le16_to_cpu(sqsh_ino->guid), &inode->i_gid);
+       if (err)
+               return err;
+
+       inode->i_ino = le32_to_cpu(sqsh_ino->inode_number);
+       inode->i_mtime.tv_sec = le32_to_cpu(sqsh_ino->mtime);
+       inode->i_atime.tv_sec = inode->i_mtime.tv_sec;
+       inode->i_ctime.tv_sec = inode->i_mtime.tv_sec;
+       inode->i_mode = le16_to_cpu(sqsh_ino->mode);
+       inode->i_size = 0;
+
+       return err;
+}
+
+
+struct inode *squashfs_iget(struct super_block *sb, long long ino,
+                               unsigned int ino_number)
+{
+       struct inode *inode = iget_locked(sb, ino_number);
+       int err;
+
+       TRACE("Entered squashfs_iget\n");
+
+       if (!inode)
+               return ERR_PTR(-ENOMEM);
+       if (!(inode->i_state & I_NEW))
+               return inode;
+
+       err = squashfs_read_inode(inode, ino);
+       if (err) {
+               iget_failed(inode);
+               return ERR_PTR(err);
+       }
+
+       unlock_new_inode(inode);
+       return inode;
+}
+
+
+/*
+ * Initialise VFS inode by reading inode from inode table (compressed
+ * metadata).  The format and amount of data read depends on type.
+ */
+int squashfs_read_inode(struct inode *inode, long long ino)
+{
+       struct super_block *sb = inode->i_sb;
+       struct squashfs_sb_info *msblk = sb->s_fs_info;
+       u64 block = SQUASHFS_INODE_BLK(ino) + msblk->inode_table;
+       int err, type, offset = SQUASHFS_INODE_OFFSET(ino);
+       union squashfs_inode squashfs_ino;
+       struct squashfs_base_inode *sqshb_ino = &squashfs_ino.base;
+
+       TRACE("Entered squashfs_read_inode\n");
+
+       /*
+        * Read inode base common to all inode types.
+        */
+       err = squashfs_read_metadata(sb, sqshb_ino, &block,
+                               &offset, sizeof(*sqshb_ino));
+       if (err < 0)
+               goto failed_read;
+
+       err = squashfs_new_inode(sb, inode, sqshb_ino);
+       if (err)
+               goto failed_read;
+
+       block = SQUASHFS_INODE_BLK(ino) + msblk->inode_table;
+       offset = SQUASHFS_INODE_OFFSET(ino);
+
+       type = le16_to_cpu(sqshb_ino->inode_type);
+       switch (type) {
+       case SQUASHFS_REG_TYPE: {
+               unsigned int frag_offset, frag_size, frag;
+               u64 frag_blk;
+               struct squashfs_reg_inode *sqsh_ino = &squashfs_ino.reg;
+
+               err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
+                                                       sizeof(*sqsh_ino));
+               if (err < 0)
+                       goto failed_read;
+
+               frag = le32_to_cpu(sqsh_ino->fragment);
+               if (frag != SQUASHFS_INVALID_FRAG) {
+                       frag_offset = le32_to_cpu(sqsh_ino->offset);
+                       frag_size = squashfs_frag_lookup(sb, frag, &frag_blk);
+                       if (frag_size < 0) {
+                               err = frag_size;
+                               goto failed_read;
+                       }
+               } else {
+                       frag_blk = SQUASHFS_INVALID_BLK;
+                       frag_size = 0;
+                       frag_offset = 0;
+               }
+
+               inode->i_nlink = 1;
+               inode->i_size = le32_to_cpu(sqsh_ino->file_size);
+               inode->i_fop = &generic_ro_fops;
+               inode->i_mode |= S_IFREG;
+               inode->i_blocks = ((inode->i_size - 1) >> 9) + 1;
+               squashfs_i(inode)->fragment_block = frag_blk;
+               squashfs_i(inode)->fragment_size = frag_size;
+               squashfs_i(inode)->fragment_offset = frag_offset;
+               squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
+               squashfs_i(inode)->block_list_start = block;
+               squashfs_i(inode)->offset = offset;
+               inode->i_data.a_ops = &squashfs_aops;
+
+               TRACE("File inode %x:%x, start_block %llx, block_list_start "
+                       "%llx, offset %x\n", SQUASHFS_INODE_BLK(ino),
+                       offset, squashfs_i(inode)->start, block, offset);
+               break;
+       }
+       case SQUASHFS_LREG_TYPE: {
+               unsigned int frag_offset, frag_size, frag;
+               u64 frag_blk;
+               struct squashfs_lreg_inode *sqsh_ino = &squashfs_ino.lreg;
+
+               err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
+                                                       sizeof(*sqsh_ino));
+               if (err < 0)
+                       goto failed_read;
+
+               frag = le32_to_cpu(sqsh_ino->fragment);
+               if (frag != SQUASHFS_INVALID_FRAG) {
+                       frag_offset = le32_to_cpu(sqsh_ino->offset);
+                       frag_size = squashfs_frag_lookup(sb, frag, &frag_blk);
+                       if (frag_size < 0) {
+                               err = frag_size;
+                               goto failed_read;
+                       }
+               } else {
+                       frag_blk = SQUASHFS_INVALID_BLK;
+                       frag_size = 0;
+                       frag_offset = 0;
+               }
+
+               inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
+               inode->i_size = le64_to_cpu(sqsh_ino->file_size);
+               inode->i_fop = &generic_ro_fops;
+               inode->i_mode |= S_IFREG;
+               inode->i_blocks = ((inode->i_size -
+                               le64_to_cpu(sqsh_ino->sparse) - 1) >> 9) + 1;
+
+               squashfs_i(inode)->fragment_block = frag_blk;
+               squashfs_i(inode)->fragment_size = frag_size;
+               squashfs_i(inode)->fragment_offset = frag_offset;
+               squashfs_i(inode)->start = le64_to_cpu(sqsh_ino->start_block);
+               squashfs_i(inode)->block_list_start = block;
+               squashfs_i(inode)->offset = offset;
+               inode->i_data.a_ops = &squashfs_aops;
+
+               TRACE("File inode %x:%x, start_block %llx, block_list_start "
+                       "%llx, offset %x\n", SQUASHFS_INODE_BLK(ino),
+                       offset, squashfs_i(inode)->start, block, offset);
+               break;
+       }
+       case SQUASHFS_DIR_TYPE: {
+               struct squashfs_dir_inode *sqsh_ino = &squashfs_ino.dir;
+
+               err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
+                               sizeof(*sqsh_ino));
+               if (err < 0)
+                       goto failed_read;
+
+               inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
+               inode->i_size = le16_to_cpu(sqsh_ino->file_size);
+               inode->i_op = &squashfs_dir_inode_ops;
+               inode->i_fop = &squashfs_dir_ops;
+               inode->i_mode |= S_IFDIR;
+               squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
+               squashfs_i(inode)->offset = le16_to_cpu(sqsh_ino->offset);
+               squashfs_i(inode)->dir_idx_cnt = 0;
+               squashfs_i(inode)->parent = le32_to_cpu(sqsh_ino->parent_inode);
+
+               TRACE("Directory inode %x:%x, start_block %llx, offset %x\n",
+                               SQUASHFS_INODE_BLK(ino), offset,
+                               squashfs_i(inode)->start,
+                               le16_to_cpu(sqsh_ino->offset));
+               break;
+       }
+       case SQUASHFS_LDIR_TYPE: {
+               struct squashfs_ldir_inode *sqsh_ino = &squashfs_ino.ldir;
+
+               err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
+                               sizeof(*sqsh_ino));
+               if (err < 0)
+                       goto failed_read;
+
+               inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
+               inode->i_size = le32_to_cpu(sqsh_ino->file_size);
+               inode->i_op = &squashfs_dir_inode_ops;
+               inode->i_fop = &squashfs_dir_ops;
+               inode->i_mode |= S_IFDIR;
+               squashfs_i(inode)->start = le32_to_cpu(sqsh_ino->start_block);
+               squashfs_i(inode)->offset = le16_to_cpu(sqsh_ino->offset);
+               squashfs_i(inode)->dir_idx_start = block;
+               squashfs_i(inode)->dir_idx_offset = offset;
+               squashfs_i(inode)->dir_idx_cnt = le16_to_cpu(sqsh_ino->i_count);
+               squashfs_i(inode)->parent = le32_to_cpu(sqsh_ino->parent_inode);
+
+               TRACE("Long directory inode %x:%x, start_block %llx, offset "
+                               "%x\n", SQUASHFS_INODE_BLK(ino), offset,
+                               squashfs_i(inode)->start,
+                               le16_to_cpu(sqsh_ino->offset));
+               break;
+       }
+       case SQUASHFS_SYMLINK_TYPE:
+       case SQUASHFS_LSYMLINK_TYPE: {
+               struct squashfs_symlink_inode *sqsh_ino = &squashfs_ino.symlink;
+
+               err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
+                               sizeof(*sqsh_ino));
+               if (err < 0)
+                       goto failed_read;
+
+               inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
+               inode->i_size = le32_to_cpu(sqsh_ino->symlink_size);
+               inode->i_op = &page_symlink_inode_operations;
+               inode->i_data.a_ops = &squashfs_symlink_aops;
+               inode->i_mode |= S_IFLNK;
+               squashfs_i(inode)->start = block;
+               squashfs_i(inode)->offset = offset;
+
+               TRACE("Symbolic link inode %x:%x, start_block %llx, offset "
+                               "%x\n", SQUASHFS_INODE_BLK(ino), offset,
+                               block, offset);
+               break;
+       }
+       case SQUASHFS_BLKDEV_TYPE:
+       case SQUASHFS_CHRDEV_TYPE:
+       case SQUASHFS_LBLKDEV_TYPE:
+       case SQUASHFS_LCHRDEV_TYPE: {
+               struct squashfs_dev_inode *sqsh_ino = &squashfs_ino.dev;
+               unsigned int rdev;
+
+               err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
+                               sizeof(*sqsh_ino));
+               if (err < 0)
+                       goto failed_read;
+
+               if (type == SQUASHFS_CHRDEV_TYPE)
+                       inode->i_mode |= S_IFCHR;
+               else
+                       inode->i_mode |= S_IFBLK;
+               inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
+               rdev = le32_to_cpu(sqsh_ino->rdev);
+               init_special_inode(inode, inode->i_mode, new_decode_dev(rdev));
+
+               TRACE("Device inode %x:%x, rdev %x\n",
+                               SQUASHFS_INODE_BLK(ino), offset, rdev);
+               break;
+       }
+       case SQUASHFS_FIFO_TYPE:
+       case SQUASHFS_SOCKET_TYPE:
+       case SQUASHFS_LFIFO_TYPE:
+       case SQUASHFS_LSOCKET_TYPE: {
+               struct squashfs_ipc_inode *sqsh_ino = &squashfs_ino.ipc;
+
+               err = squashfs_read_metadata(sb, sqsh_ino, &block, &offset,
+                               sizeof(*sqsh_ino));
+               if (err < 0)
+                       goto failed_read;
+
+               if (type == SQUASHFS_FIFO_TYPE)
+                       inode->i_mode |= S_IFIFO;
+               else
+                       inode->i_mode |= S_IFSOCK;
+               inode->i_nlink = le32_to_cpu(sqsh_ino->nlink);
+               init_special_inode(inode, inode->i_mode, 0);
+               break;
+       }
+       default:
+               ERROR("Unknown inode type %d in squashfs_iget!\n", type);
+               return -EINVAL;
+       }
+
+       return 0;
+
+failed_read:
+       ERROR("Unable to read inode 0x%llx\n", ino);
+       return err;
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/namei.c b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/namei.c
new file mode 100644 (file)
index 0000000..9e39865
--- /dev/null
@@ -0,0 +1,242 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * namei.c
+ */
+
+/*
+ * This file implements code to do filename lookup in directories.
+ *
+ * Like inodes, directories are packed into compressed metadata blocks, stored
+ * in a directory table.  Directories are accessed using the start address of
+ * the metablock containing the directory and the offset into the
+ * decompressed block (<block, offset>).
+ *
+ * Directories are organised in a slightly complex way, and are not simply
+ * a list of file names.  The organisation takes advantage of the
+ * fact that (in most cases) the inodes of the files will be in the same
+ * compressed metadata block, and therefore, can share the start block.
+ * Directories are therefore organised in a two level list, a directory
+ * header containing the shared start block value, and a sequence of directory
+ * entries, each of which share the shared start block.  A new directory header
+ * is written once/if the inode start block changes.  The directory
+ * header/directory entry list is repeated as many times as necessary.
+ *
+ * Directories are sorted, and can contain a directory index to speed up
+ * file lookup.  Directory indexes store one entry per metablock, each entry
+ * storing the index/filename mapping to the first directory header
+ * in each metadata block.  Directories are sorted in alphabetical order,
+ * and at lookup the index is scanned linearly looking for the first filename
+ * alphabetically larger than the filename being looked up.  At this point the
+ * location of the metadata block the filename is in has been found.
+ * The general idea of the index is ensure only one metadata block needs to be
+ * decompressed to do a lookup irrespective of the length of the directory.
+ * This scheme has the advantage that it doesn't require extra memory overhead
+ * and doesn't require much extra storage on disk.
+ */
+
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/dcache.h>
+#include <linux/zlib.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+
+/*
+ * Lookup name in the directory index, returning the location of the metadata
+ * block containing it, and the directory index this represents.
+ *
+ * If we get an error reading the index then return the part of the index
+ * (if any) we have managed to read - the index isn't essential, just
+ * quicker.
+ */
+static int get_dir_index_using_name(struct super_block *sb,
+                       u64 *next_block, int *next_offset, u64 index_start,
+                       int index_offset, int i_count, const char *name,
+                       int len)
+{
+       struct squashfs_sb_info *msblk = sb->s_fs_info;
+       int i, size, length = 0, err;
+       struct squashfs_dir_index *index;
+       char *str;
+
+       TRACE("Entered get_dir_index_using_name, i_count %d\n", i_count);
+
+       index = kmalloc(sizeof(*index) + SQUASHFS_NAME_LEN * 2 + 2, GFP_KERNEL);
+       if (index == NULL) {
+               ERROR("Failed to allocate squashfs_dir_index\n");
+               goto out;
+       }
+
+       str = &index->name[SQUASHFS_NAME_LEN + 1];
+       strncpy(str, name, len);
+       str[len] = '\0';
+
+       for (i = 0; i < i_count; i++) {
+               err = squashfs_read_metadata(sb, index, &index_start,
+                                       &index_offset, sizeof(*index));
+               if (err < 0)
+                       break;
+
+
+               size = le32_to_cpu(index->size) + 1;
+
+               err = squashfs_read_metadata(sb, index->name, &index_start,
+                                       &index_offset, size);
+               if (err < 0)
+                       break;
+
+               index->name[size] = '\0';
+
+               if (strcmp(index->name, str) > 0)
+                       break;
+
+               length = le32_to_cpu(index->index);
+               *next_block = le32_to_cpu(index->start_block) +
+                                       msblk->directory_table;
+       }
+
+       *next_offset = (length + *next_offset) % SQUASHFS_METADATA_SIZE;
+       kfree(index);
+
+out:
+       /*
+        * Return index (f_pos) of the looked up metadata block.  Translate
+        * from internal f_pos to external f_pos which is offset by 3 because
+        * we invent "." and ".." entries which are not actually stored in the
+        * directory.
+        */
+       return length + 3;
+}
+
+
+static struct dentry *squashfs_lookup(struct inode *dir, struct dentry *dentry,
+                                struct nameidata *nd)
+{
+       const unsigned char *name = dentry->d_name.name;
+       int len = dentry->d_name.len;
+       struct inode *inode = NULL;
+       struct squashfs_sb_info *msblk = dir->i_sb->s_fs_info;
+       struct squashfs_dir_header dirh;
+       struct squashfs_dir_entry *dire;
+       u64 block = squashfs_i(dir)->start + msblk->directory_table;
+       int offset = squashfs_i(dir)->offset;
+       int err, length = 0, dir_count, size;
+
+       TRACE("Entered squashfs_lookup [%llx:%x]\n", block, offset);
+
+       dire = kmalloc(sizeof(*dire) + SQUASHFS_NAME_LEN + 1, GFP_KERNEL);
+       if (dire == NULL) {
+               ERROR("Failed to allocate squashfs_dir_entry\n");
+               return ERR_PTR(-ENOMEM);
+       }
+
+       if (len > SQUASHFS_NAME_LEN) {
+               err = -ENAMETOOLONG;
+               goto failed;
+       }
+
+       length = get_dir_index_using_name(dir->i_sb, &block, &offset,
+                               squashfs_i(dir)->dir_idx_start,
+                               squashfs_i(dir)->dir_idx_offset,
+                               squashfs_i(dir)->dir_idx_cnt, name, len);
+
+       while (length < i_size_read(dir)) {
+               /*
+                * Read directory header.
+                */
+               err = squashfs_read_metadata(dir->i_sb, &dirh, &block,
+                               &offset, sizeof(dirh));
+               if (err < 0)
+                       goto read_failure;
+
+               length += sizeof(dirh);
+
+               dir_count = le32_to_cpu(dirh.count) + 1;
+               while (dir_count--) {
+                       /*
+                        * Read directory entry.
+                        */
+                       err = squashfs_read_metadata(dir->i_sb, dire, &block,
+                                       &offset, sizeof(*dire));
+                       if (err < 0)
+                               goto read_failure;
+
+                       size = le16_to_cpu(dire->size) + 1;
+
+                       err = squashfs_read_metadata(dir->i_sb, dire->name,
+                                       &block, &offset, size);
+                       if (err < 0)
+                               goto read_failure;
+
+                       length += sizeof(*dire) + size;
+
+                       if (name[0] < dire->name[0])
+                               goto exit_lookup;
+
+                       if (len == size && !strncmp(name, dire->name, len)) {
+                               unsigned int blk, off, ino_num;
+                               long long ino;
+                               blk = le32_to_cpu(dirh.start_block);
+                               off = le16_to_cpu(dire->offset);
+                               ino_num = le32_to_cpu(dirh.inode_number) +
+                                       (short) le16_to_cpu(dire->inode_number);
+                               ino = SQUASHFS_MKINODE(blk, off);
+
+                               TRACE("calling squashfs_iget for directory "
+                                       "entry %s, inode  %x:%x, %d\n", name,
+                                       blk, off, ino_num);
+
+                               inode = squashfs_iget(dir->i_sb, ino, ino_num);
+                               if (IS_ERR(inode)) {
+                                       err = PTR_ERR(inode);
+                                       goto failed;
+                               }
+
+                               goto exit_lookup;
+                       }
+               }
+       }
+
+exit_lookup:
+       kfree(dire);
+       if (inode)
+               return d_splice_alias(inode, dentry);
+       d_add(dentry, inode);
+       return ERR_PTR(0);
+
+read_failure:
+       ERROR("Unable to read directory block [%llx:%x]\n",
+               squashfs_i(dir)->start + msblk->directory_table,
+               squashfs_i(dir)->offset);
+failed:
+       kfree(dire);
+       return ERR_PTR(err);
+}
+
+
+const struct inode_operations squashfs_dir_inode_ops = {
+       .lookup = squashfs_lookup
+};
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs.h b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs.h
new file mode 100644 (file)
index 0000000..6b2515d
--- /dev/null
@@ -0,0 +1,90 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * squashfs.h
+ */
+
+#define TRACE(s, args...)      pr_debug("SQUASHFS: "s, ## args)
+
+#define ERROR(s, args...)      pr_err("SQUASHFS error: "s, ## args)
+
+#define WARNING(s, args...)    pr_warning("SQUASHFS: "s, ## args)
+
+static inline struct squashfs_inode_info *squashfs_i(struct inode *inode)
+{
+       return list_entry(inode, struct squashfs_inode_info, vfs_inode);
+}
+
+/* block.c */
+extern int squashfs_read_data(struct super_block *, void **, u64, int, u64 *,
+                               int);
+
+/* cache.c */
+extern struct squashfs_cache *squashfs_cache_init(char *, int, int);
+extern void squashfs_cache_delete(struct squashfs_cache *);
+extern struct squashfs_cache_entry *squashfs_cache_get(struct super_block *,
+                               struct squashfs_cache *, u64, int);
+extern void squashfs_cache_put(struct squashfs_cache_entry *);
+extern int squashfs_copy_data(void *, struct squashfs_cache_entry *, int, int);
+extern int squashfs_read_metadata(struct super_block *, void *, u64 *,
+                               int *, int);
+extern struct squashfs_cache_entry *squashfs_get_fragment(struct super_block *,
+                               u64, int);
+extern struct squashfs_cache_entry *squashfs_get_datablock(struct super_block *,
+                               u64, int);
+extern int squashfs_read_table(struct super_block *, void *, u64, int);
+
+/* export.c */
+extern __le64 *squashfs_read_inode_lookup_table(struct super_block *, u64,
+                               unsigned int);
+
+/* fragment.c */
+extern int squashfs_frag_lookup(struct super_block *, unsigned int, u64 *);
+extern __le64 *squashfs_read_fragment_index_table(struct super_block *,
+                               u64, unsigned int);
+
+/* id.c */
+extern int squashfs_get_id(struct super_block *, unsigned int, unsigned int *);
+extern __le64 *squashfs_read_id_index_table(struct super_block *, u64,
+                               unsigned short);
+
+/* inode.c */
+extern struct inode *squashfs_iget(struct super_block *, long long,
+                               unsigned int);
+extern int squashfs_read_inode(struct inode *, long long);
+
+/*
+ * Inodes and files operations
+ */
+
+/* dir.c */
+extern const struct file_operations squashfs_dir_ops;
+
+/* export.c */
+extern const struct export_operations squashfs_export_ops;
+
+/* file.c */
+extern const struct address_space_operations squashfs_aops;
+
+/* namei.c */
+extern const struct inode_operations squashfs_dir_inode_ops;
+
+/* symlink.c */
+extern const struct address_space_operations squashfs_symlink_aops;
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs_fs.h b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs_fs.h
new file mode 100644 (file)
index 0000000..6840da1
--- /dev/null
@@ -0,0 +1,381 @@
+#ifndef SQUASHFS_FS
+#define SQUASHFS_FS
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * squashfs_fs.h
+ */
+
+#define SQUASHFS_CACHED_FRAGMENTS      CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
+#define SQUASHFS_MAJOR                 4
+#define SQUASHFS_MINOR                 0
+#define SQUASHFS_MAGIC                 0x73717368
+#define SQUASHFS_START                 0
+
+/* size of metadata (inode and directory) blocks */
+#define SQUASHFS_METADATA_SIZE         8192
+#define SQUASHFS_METADATA_LOG          13
+
+/* default size of data blocks */
+#define SQUASHFS_FILE_SIZE             131072
+#define SQUASHFS_FILE_LOG              17
+
+#define SQUASHFS_FILE_MAX_SIZE         1048576
+#define SQUASHFS_FILE_MAX_LOG          20
+
+/* Max number of uids and gids */
+#define SQUASHFS_IDS                   65536
+
+/* Max length of filename (not 255) */
+#define SQUASHFS_NAME_LEN              256
+
+#define SQUASHFS_INVALID_FRAG          (0xffffffffU)
+#define SQUASHFS_INVALID_BLK           (-1LL)
+
+/* Filesystem flags */
+#define SQUASHFS_NOI                   0
+#define SQUASHFS_NOD                   1
+#define SQUASHFS_NOF                   3
+#define SQUASHFS_NO_FRAG               4
+#define SQUASHFS_ALWAYS_FRAG           5
+#define SQUASHFS_DUPLICATE             6
+#define SQUASHFS_EXPORT                        7
+
+#define SQUASHFS_BIT(flag, bit)                ((flag >> bit) & 1)
+
+#define SQUASHFS_UNCOMPRESSED_INODES(flags)    SQUASHFS_BIT(flags, \
+                                               SQUASHFS_NOI)
+
+#define SQUASHFS_UNCOMPRESSED_DATA(flags)      SQUASHFS_BIT(flags, \
+                                               SQUASHFS_NOD)
+
+#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
+                                               SQUASHFS_NOF)
+
+#define SQUASHFS_NO_FRAGMENTS(flags)           SQUASHFS_BIT(flags, \
+                                               SQUASHFS_NO_FRAG)
+
+#define SQUASHFS_ALWAYS_FRAGMENTS(flags)       SQUASHFS_BIT(flags, \
+                                               SQUASHFS_ALWAYS_FRAG)
+
+#define SQUASHFS_DUPLICATES(flags)             SQUASHFS_BIT(flags, \
+                                               SQUASHFS_DUPLICATE)
+
+#define SQUASHFS_EXPORTABLE(flags)             SQUASHFS_BIT(flags, \
+                                               SQUASHFS_EXPORT)
+
+/* Max number of types and file types */
+#define SQUASHFS_DIR_TYPE              1
+#define SQUASHFS_REG_TYPE              2
+#define SQUASHFS_SYMLINK_TYPE          3
+#define SQUASHFS_BLKDEV_TYPE           4
+#define SQUASHFS_CHRDEV_TYPE           5
+#define SQUASHFS_FIFO_TYPE             6
+#define SQUASHFS_SOCKET_TYPE           7
+#define SQUASHFS_LDIR_TYPE             8
+#define SQUASHFS_LREG_TYPE             9
+#define SQUASHFS_LSYMLINK_TYPE         10
+#define SQUASHFS_LBLKDEV_TYPE          11
+#define SQUASHFS_LCHRDEV_TYPE          12
+#define SQUASHFS_LFIFO_TYPE            13
+#define SQUASHFS_LSOCKET_TYPE          14
+
+/* Flag whether block is compressed or uncompressed, bit is set if block is
+ * uncompressed */
+#define SQUASHFS_COMPRESSED_BIT                (1 << 15)
+
+#define SQUASHFS_COMPRESSED_SIZE(B)    (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
+               (B) & ~SQUASHFS_COMPRESSED_BIT :  SQUASHFS_COMPRESSED_BIT)
+
+#define SQUASHFS_COMPRESSED(B)         (!((B) & SQUASHFS_COMPRESSED_BIT))
+
+#define SQUASHFS_COMPRESSED_BIT_BLOCK  (1 << 24)
+
+#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B)      ((B) & \
+                                               ~SQUASHFS_COMPRESSED_BIT_BLOCK)
+
+#define SQUASHFS_COMPRESSED_BLOCK(B)   (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
+
+/*
+ * Inode number ops.  Inodes consist of a compressed block number, and an
+ * uncompressed offset within that block
+ */
+#define SQUASHFS_INODE_BLK(A)          ((unsigned int) ((A) >> 16))
+
+#define SQUASHFS_INODE_OFFSET(A)       ((unsigned int) ((A) & 0xffff))
+
+#define SQUASHFS_MKINODE(A, B)         ((long long)(((long long) (A)\
+                                       << 16) + (B)))
+
+/* Translate between VFS mode and squashfs mode */
+#define SQUASHFS_MODE(A)               ((A) & 0xfff)
+
+/* fragment and fragment table defines */
+#define SQUASHFS_FRAGMENT_BYTES(A)     \
+                               ((A) * sizeof(struct squashfs_fragment_entry))
+
+#define SQUASHFS_FRAGMENT_INDEX(A)     (SQUASHFS_FRAGMENT_BYTES(A) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A)      (SQUASHFS_FRAGMENT_BYTES(A) % \
+                                               SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEXES(A)   ((SQUASHFS_FRAGMENT_BYTES(A) + \
+                                       SQUASHFS_METADATA_SIZE - 1) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_BYTES(A)       (SQUASHFS_FRAGMENT_INDEXES(A) *\
+                                               sizeof(u64))
+
+/* inode lookup table defines */
+#define SQUASHFS_LOOKUP_BYTES(A)       ((A) * sizeof(u64))
+
+#define SQUASHFS_LOOKUP_BLOCK(A)       (SQUASHFS_LOOKUP_BYTES(A) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A)        (SQUASHFS_LOOKUP_BYTES(A) % \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_LOOKUP_BLOCKS(A)      ((SQUASHFS_LOOKUP_BYTES(A) + \
+                                       SQUASHFS_METADATA_SIZE - 1) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_LOOKUP_BLOCK_BYTES(A) (SQUASHFS_LOOKUP_BLOCKS(A) *\
+                                       sizeof(u64))
+
+/* uid/gid lookup table defines */
+#define SQUASHFS_ID_BYTES(A)           ((A) * sizeof(unsigned int))
+
+#define SQUASHFS_ID_BLOCK(A)           (SQUASHFS_ID_BYTES(A) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_ID_BLOCK_OFFSET(A)    (SQUASHFS_ID_BYTES(A) % \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_ID_BLOCKS(A)          ((SQUASHFS_ID_BYTES(A) + \
+                                       SQUASHFS_METADATA_SIZE - 1) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_ID_BLOCK_BYTES(A)     (SQUASHFS_ID_BLOCKS(A) *\
+                                       sizeof(u64))
+
+/* cached data constants for filesystem */
+#define SQUASHFS_CACHED_BLKS           8
+
+#define SQUASHFS_MAX_FILE_SIZE_LOG     64
+
+#define SQUASHFS_MAX_FILE_SIZE         (1LL << \
+                                       (SQUASHFS_MAX_FILE_SIZE_LOG - 2))
+
+#define SQUASHFS_MARKER_BYTE           0xff
+
+/* meta index cache */
+#define SQUASHFS_META_INDEXES  (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
+#define SQUASHFS_META_ENTRIES  127
+#define SQUASHFS_META_SLOTS    8
+
+struct meta_entry {
+       u64                     data_block;
+       unsigned int            index_block;
+       unsigned short          offset;
+       unsigned short          pad;
+};
+
+struct meta_index {
+       unsigned int            inode_number;
+       unsigned int            offset;
+       unsigned short          entries;
+       unsigned short          skip;
+       unsigned short          locked;
+       unsigned short          pad;
+       struct meta_entry       meta_entry[SQUASHFS_META_ENTRIES];
+};
+
+
+/*
+ * definitions for structures on disk
+ */
+#define ZLIB_COMPRESSION        1
+
+struct squashfs_super_block {
+       __le32                  s_magic;
+       __le32                  inodes;
+       __le32                  mkfs_time;
+       __le32                  block_size;
+       __le32                  fragments;
+       __le16                  compression;
+       __le16                  block_log;
+       __le16                  flags;
+       __le16                  no_ids;
+       __le16                  s_major;
+       __le16                  s_minor;
+       __le64                  root_inode;
+       __le64                  bytes_used;
+       __le64                  id_table_start;
+       __le64                  xattr_table_start;
+       __le64                  inode_table_start;
+       __le64                  directory_table_start;
+       __le64                  fragment_table_start;
+       __le64                  lookup_table_start;
+};
+
+struct squashfs_dir_index {
+       __le32                  index;
+       __le32                  start_block;
+       __le32                  size;
+       unsigned char           name[0];
+};
+
+struct squashfs_base_inode {
+       __le16                  inode_type;
+       __le16                  mode;
+       __le16                  uid;
+       __le16                  guid;
+       __le32                  mtime;
+       __le32                  inode_number;
+};
+
+struct squashfs_ipc_inode {
+       __le16                  inode_type;
+       __le16                  mode;
+       __le16                  uid;
+       __le16                  guid;
+       __le32                  mtime;
+       __le32                  inode_number;
+       __le32                  nlink;
+};
+
+struct squashfs_dev_inode {
+       __le16                  inode_type;
+       __le16                  mode;
+       __le16                  uid;
+       __le16                  guid;
+       __le32                  mtime;
+       __le32                  inode_number;
+       __le32                  nlink;
+       __le32                  rdev;
+};
+
+struct squashfs_symlink_inode {
+       __le16                  inode_type;
+       __le16                  mode;
+       __le16                  uid;
+       __le16                  guid;
+       __le32                  mtime;
+       __le32                  inode_number;
+       __le32                  nlink;
+       __le32                  symlink_size;
+       char                    symlink[0];
+};
+
+struct squashfs_reg_inode {
+       __le16                  inode_type;
+       __le16                  mode;
+       __le16                  uid;
+       __le16                  guid;
+       __le32                  mtime;
+       __le32                  inode_number;
+       __le32                  start_block;
+       __le32                  fragment;
+       __le32                  offset;
+       __le32                  file_size;
+       __le16                  block_list[0];
+};
+
+struct squashfs_lreg_inode {
+       __le16                  inode_type;
+       __le16                  mode;
+       __le16                  uid;
+       __le16                  guid;
+       __le32                  mtime;
+       __le32                  inode_number;
+       __le64                  start_block;
+       __le64                  file_size;
+       __le64                  sparse;
+       __le32                  nlink;
+       __le32                  fragment;
+       __le32                  offset;
+       __le32                  xattr;
+       __le16                  block_list[0];
+};
+
+struct squashfs_dir_inode {
+       __le16                  inode_type;
+       __le16                  mode;
+       __le16                  uid;
+       __le16                  guid;
+       __le32                  mtime;
+       __le32                  inode_number;
+       __le32                  start_block;
+       __le32                  nlink;
+       __le16                  file_size;
+       __le16                  offset;
+       __le32                  parent_inode;
+};
+
+struct squashfs_ldir_inode {
+       __le16                  inode_type;
+       __le16                  mode;
+       __le16                  uid;
+       __le16                  guid;
+       __le32                  mtime;
+       __le32                  inode_number;
+       __le32                  nlink;
+       __le32                  file_size;
+       __le32                  start_block;
+       __le32                  parent_inode;
+       __le16                  i_count;
+       __le16                  offset;
+       __le32                  xattr;
+       struct squashfs_dir_index       index[0];
+};
+
+union squashfs_inode {
+       struct squashfs_base_inode              base;
+       struct squashfs_dev_inode               dev;
+       struct squashfs_symlink_inode           symlink;
+       struct squashfs_reg_inode               reg;
+       struct squashfs_lreg_inode              lreg;
+       struct squashfs_dir_inode               dir;
+       struct squashfs_ldir_inode              ldir;
+       struct squashfs_ipc_inode               ipc;
+};
+
+struct squashfs_dir_entry {
+       __le16                  offset;
+       __le16                  inode_number;
+       __le16                  type;
+       __le16                  size;
+       char                    name[0];
+};
+
+struct squashfs_dir_header {
+       __le32                  count;
+       __le32                  start_block;
+       __le32                  inode_number;
+};
+
+struct squashfs_fragment_entry {
+       __le64                  start_block;
+       __le32                  size;
+       unsigned int            unused;
+};
+
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs_fs_i.h b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs_fs_i.h
new file mode 100644 (file)
index 0000000..fbfca30
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef SQUASHFS_FS_I
+#define SQUASHFS_FS_I
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * squashfs_fs_i.h
+ */
+
+struct squashfs_inode_info {
+       u64             start;
+       int             offset;
+       union {
+               struct {
+                       u64             fragment_block;
+                       int             fragment_size;
+                       int             fragment_offset;
+                       u64             block_list_start;
+               };
+               struct {
+                       u64             dir_idx_start;
+                       int             dir_idx_offset;
+                       int             dir_idx_cnt;
+                       int             parent;
+               };
+       };
+       struct inode    vfs_inode;
+};
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs_fs_sb.h b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/squashfs_fs_sb.h
new file mode 100644 (file)
index 0000000..c8c6561
--- /dev/null
@@ -0,0 +1,76 @@
+#ifndef SQUASHFS_FS_SB
+#define SQUASHFS_FS_SB
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * squashfs_fs_sb.h
+ */
+
+#include "squashfs_fs.h"
+
+struct squashfs_cache {
+       char                    *name;
+       int                     entries;
+       int                     next_blk;
+       int                     num_waiters;
+       int                     unused;
+       int                     block_size;
+       int                     pages;
+       spinlock_t              lock;
+       wait_queue_head_t       wait_queue;
+       struct squashfs_cache_entry *entry;
+};
+
+struct squashfs_cache_entry {
+       u64                     block;
+       int                     length;
+       int                     refcount;
+       u64                     next_index;
+       int                     pending;
+       int                     error;
+       int                     num_waiters;
+       wait_queue_head_t       wait_queue;
+       struct squashfs_cache   *cache;
+       void                    **data;
+};
+
+struct squashfs_sb_info {
+       int                     devblksize;
+       int                     devblksize_log2;
+       struct squashfs_cache   *block_cache;
+       struct squashfs_cache   *fragment_cache;
+       struct squashfs_cache   *read_page;
+       int                     next_meta_index;
+       __le64                  *id_table;
+       __le64                  *fragment_index;
+       unsigned int            *fragment_index_2;
+       struct mutex            read_data_mutex;
+       struct mutex            meta_index_mutex;
+       struct meta_index       *meta_index;
+       z_stream                stream;
+       __le64                  *inode_lookup_table;
+       u64                     inode_table;
+       u64                     directory_table;
+       unsigned int            block_size;
+       unsigned short          block_log;
+       long long               bytes_used;
+       unsigned int            inodes;
+};
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/super.c b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/super.c
new file mode 100644 (file)
index 0000000..a0466d7
--- /dev/null
@@ -0,0 +1,440 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * super.c
+ */
+
+/*
+ * This file implements code to read the superblock, read and initialise
+ * in-memory structures at mount time, and all the VFS glue code to register
+ * the filesystem.
+ */
+
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/slab.h>
+#include <linux/mutex.h>
+#include <linux/pagemap.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/zlib.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+
+static struct file_system_type squashfs_fs_type;
+static struct super_operations squashfs_super_ops;
+
+static int supported_squashfs_filesystem(short major, short minor, short comp)
+{
+       if (major < SQUASHFS_MAJOR) {
+               ERROR("Major/Minor mismatch, older Squashfs %d.%d "
+                       "filesystems are unsupported\n", major, minor);
+               return -EINVAL;
+       } else if (major > SQUASHFS_MAJOR || minor > SQUASHFS_MINOR) {
+               ERROR("Major/Minor mismatch, trying to mount newer "
+                       "%d.%d filesystem\n", major, minor);
+               ERROR("Please update your kernel\n");
+               return -EINVAL;
+       }
+
+       if (comp != ZLIB_COMPRESSION)
+               return -EINVAL;
+
+       return 0;
+}
+
+
+static int squashfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+       struct squashfs_sb_info *msblk;
+       struct squashfs_super_block *sblk = NULL;
+       char b[BDEVNAME_SIZE];
+       struct inode *root;
+       long long root_inode;
+       unsigned short flags;
+       unsigned int fragments;
+       u64 lookup_table_start;
+       int err;
+
+       TRACE("Entered squashfs_fill_superblock\n");
+
+       sb->s_fs_info = kzalloc(sizeof(*msblk), GFP_KERNEL);
+       if (sb->s_fs_info == NULL) {
+               ERROR("Failed to allocate squashfs_sb_info\n");
+               return -ENOMEM;
+       }
+       msblk = sb->s_fs_info;
+
+       msblk->stream.workspace = kmalloc(zlib_inflate_workspacesize(),
+               GFP_KERNEL);
+       if (msblk->stream.workspace == NULL) {
+               ERROR("Failed to allocate zlib workspace\n");
+               goto failure;
+       }
+
+       sblk = kzalloc(sizeof(*sblk), GFP_KERNEL);
+       if (sblk == NULL) {
+               ERROR("Failed to allocate squashfs_super_block\n");
+               goto failure;
+       }
+
+       msblk->devblksize = sb_min_blocksize(sb, BLOCK_SIZE);
+       msblk->devblksize_log2 = ffz(~msblk->devblksize);
+
+       mutex_init(&msblk->read_data_mutex);
+       mutex_init(&msblk->meta_index_mutex);
+
+       /*
+        * msblk->bytes_used is checked in squashfs_read_table to ensure reads
+        * are not beyond filesystem end.  But as we're using
+        * squashfs_read_table here to read the superblock (including the value
+        * of bytes_used) we need to set it to an initial sensible dummy value
+        */
+       msblk->bytes_used = sizeof(*sblk);
+       err = squashfs_read_table(sb, sblk, SQUASHFS_START, sizeof(*sblk));
+
+       if (err < 0) {
+               ERROR("unable to read squashfs_super_block\n");
+               goto failed_mount;
+       }
+
+       /* Check it is a SQUASHFS superblock */
+       sb->s_magic = le32_to_cpu(sblk->s_magic);
+       if (sb->s_magic != SQUASHFS_MAGIC) {
+               if (!silent)
+                       ERROR("Can't find a SQUASHFS superblock on %s\n",
+                                               bdevname(sb->s_bdev, b));
+               err = -EINVAL;
+               goto failed_mount;
+       }
+
+       /* Check the MAJOR & MINOR versions and compression type */
+       err = supported_squashfs_filesystem(le16_to_cpu(sblk->s_major),
+                       le16_to_cpu(sblk->s_minor),
+                       le16_to_cpu(sblk->compression));
+       if (err < 0)
+               goto failed_mount;
+
+       err = -EINVAL;
+
+       /*
+        * Check if there's xattrs in the filesystem.  These are not
+        * supported in this version, so warn that they will be ignored.
+        */
+       if (le64_to_cpu(sblk->xattr_table_start) != SQUASHFS_INVALID_BLK)
+               ERROR("Xattrs in filesystem, these will be ignored\n");
+
+       /* Check the filesystem does not extend beyond the end of the
+          block device */
+       msblk->bytes_used = le64_to_cpu(sblk->bytes_used);
+       if (msblk->bytes_used < 0 || msblk->bytes_used >
+                       i_size_read(sb->s_bdev->bd_inode))
+               goto failed_mount;
+
+       /* Check block size for sanity */
+       msblk->block_size = le32_to_cpu(sblk->block_size);
+       if (msblk->block_size > SQUASHFS_FILE_MAX_SIZE)
+               goto failed_mount;
+
+       msblk->block_log = le16_to_cpu(sblk->block_log);
+       if (msblk->block_log > SQUASHFS_FILE_MAX_LOG)
+               goto failed_mount;
+
+       /* Check the root inode for sanity */
+       root_inode = le64_to_cpu(sblk->root_inode);
+       if (SQUASHFS_INODE_OFFSET(root_inode) > SQUASHFS_METADATA_SIZE)
+               goto failed_mount;
+
+       msblk->inode_table = le64_to_cpu(sblk->inode_table_start);
+       msblk->directory_table = le64_to_cpu(sblk->directory_table_start);
+       msblk->inodes = le32_to_cpu(sblk->inodes);
+       flags = le16_to_cpu(sblk->flags);
+
+       TRACE("Found valid superblock on %s\n", bdevname(sb->s_bdev, b));
+       TRACE("Inodes are %scompressed\n", SQUASHFS_UNCOMPRESSED_INODES(flags)
+                               ? "un" : "");
+       TRACE("Data is %scompressed\n", SQUASHFS_UNCOMPRESSED_DATA(flags)
+                               ? "un" : "");
+       TRACE("Filesystem size %lld bytes\n", msblk->bytes_used);
+       TRACE("Block size %d\n", msblk->block_size);
+       TRACE("Number of inodes %d\n", msblk->inodes);
+       TRACE("Number of fragments %d\n", le32_to_cpu(sblk->fragments));
+       TRACE("Number of ids %d\n", le16_to_cpu(sblk->no_ids));
+       TRACE("sblk->inode_table_start %llx\n", msblk->inode_table);
+       TRACE("sblk->directory_table_start %llx\n", msblk->directory_table);
+       TRACE("sblk->fragment_table_start %llx\n",
+               (u64) le64_to_cpu(sblk->fragment_table_start));
+       TRACE("sblk->id_table_start %llx\n",
+               (u64) le64_to_cpu(sblk->id_table_start));
+
+       sb->s_maxbytes = MAX_LFS_FILESIZE;
+       sb->s_flags |= MS_RDONLY;
+       sb->s_op = &squashfs_super_ops;
+
+       err = -ENOMEM;
+
+       msblk->block_cache = squashfs_cache_init("metadata",
+                       SQUASHFS_CACHED_BLKS, SQUASHFS_METADATA_SIZE);
+       if (msblk->block_cache == NULL)
+               goto failed_mount;
+
+       /* Allocate read_page block */
+       msblk->read_page = squashfs_cache_init("data", 1, msblk->block_size);
+       if (msblk->read_page == NULL) {
+               ERROR("Failed to allocate read_page block\n");
+               goto failed_mount;
+       }
+
+       /* Allocate and read id index table */
+       msblk->id_table = squashfs_read_id_index_table(sb,
+               le64_to_cpu(sblk->id_table_start), le16_to_cpu(sblk->no_ids));
+       if (IS_ERR(msblk->id_table)) {
+               err = PTR_ERR(msblk->id_table);
+               msblk->id_table = NULL;
+               goto failed_mount;
+       }
+
+       fragments = le32_to_cpu(sblk->fragments);
+       if (fragments == 0)
+               goto allocate_lookup_table;
+
+       msblk->fragment_cache = squashfs_cache_init("fragment",
+               SQUASHFS_CACHED_FRAGMENTS, msblk->block_size);
+       if (msblk->fragment_cache == NULL) {
+               err = -ENOMEM;
+               goto failed_mount;
+       }
+
+       /* Allocate and read fragment index table */
+       msblk->fragment_index = squashfs_read_fragment_index_table(sb,
+               le64_to_cpu(sblk->fragment_table_start), fragments);
+       if (IS_ERR(msblk->fragment_index)) {
+               err = PTR_ERR(msblk->fragment_index);
+               msblk->fragment_index = NULL;
+               goto failed_mount;
+       }
+
+allocate_lookup_table:
+       lookup_table_start = le64_to_cpu(sblk->lookup_table_start);
+       if (lookup_table_start == SQUASHFS_INVALID_BLK)
+               goto allocate_root;
+
+       /* Allocate and read inode lookup table */
+       msblk->inode_lookup_table = squashfs_read_inode_lookup_table(sb,
+               lookup_table_start, msblk->inodes);
+       if (IS_ERR(msblk->inode_lookup_table)) {
+               err = PTR_ERR(msblk->inode_lookup_table);
+               msblk->inode_lookup_table = NULL;
+               goto failed_mount;
+       }
+
+       sb->s_export_op = &squashfs_export_ops;
+
+allocate_root:
+       root = new_inode(sb);
+       if (!root) {
+               err = -ENOMEM;
+               goto failed_mount;
+       }
+
+       err = squashfs_read_inode(root, root_inode);
+       if (err) {
+               iget_failed(root);
+               goto failed_mount;
+       }
+       insert_inode_hash(root);
+
+       sb->s_root = d_alloc_root(root);
+       if (sb->s_root == NULL) {
+               ERROR("Root inode create failed\n");
+               err = -ENOMEM;
+               iput(root);
+               goto failed_mount;
+       }
+
+       TRACE("Leaving squashfs_fill_super\n");
+       kfree(sblk);
+       return 0;
+
+failed_mount:
+       squashfs_cache_delete(msblk->block_cache);
+       squashfs_cache_delete(msblk->fragment_cache);
+       squashfs_cache_delete(msblk->read_page);
+       kfree(msblk->inode_lookup_table);
+       kfree(msblk->fragment_index);
+       kfree(msblk->id_table);
+       kfree(msblk->stream.workspace);
+       kfree(sb->s_fs_info);
+       sb->s_fs_info = NULL;
+       kfree(sblk);
+       return err;
+
+failure:
+       kfree(msblk->stream.workspace);
+       kfree(sb->s_fs_info);
+       sb->s_fs_info = NULL;
+       return -ENOMEM;
+}
+
+
+static int squashfs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+       struct squashfs_sb_info *msblk = dentry->d_sb->s_fs_info;
+
+       TRACE("Entered squashfs_statfs\n");
+
+       buf->f_type = SQUASHFS_MAGIC;
+       buf->f_bsize = msblk->block_size;
+       buf->f_blocks = ((msblk->bytes_used - 1) >> msblk->block_log) + 1;
+       buf->f_bfree = buf->f_bavail = 0;
+       buf->f_files = msblk->inodes;
+       buf->f_ffree = 0;
+       buf->f_namelen = SQUASHFS_NAME_LEN;
+
+       return 0;
+}
+
+
+static int squashfs_remount(struct super_block *sb, int *flags, char *data)
+{
+       *flags |= MS_RDONLY;
+       return 0;
+}
+
+
+static void squashfs_put_super(struct super_block *sb)
+{
+       if (sb->s_fs_info) {
+               struct squashfs_sb_info *sbi = sb->s_fs_info;
+               squashfs_cache_delete(sbi->block_cache);
+               squashfs_cache_delete(sbi->fragment_cache);
+               squashfs_cache_delete(sbi->read_page);
+               kfree(sbi->id_table);
+               kfree(sbi->fragment_index);
+               kfree(sbi->meta_index);
+               kfree(sbi->stream.workspace);
+               kfree(sb->s_fs_info);
+               sb->s_fs_info = NULL;
+       }
+}
+
+
+static int squashfs_get_sb(struct file_system_type *fs_type, int flags,
+                               const char *dev_name, void *data,
+                               struct vfsmount *mnt)
+{
+       return get_sb_bdev(fs_type, flags, dev_name, data, squashfs_fill_super,
+                               mnt);
+}
+
+
+static struct kmem_cache *squashfs_inode_cachep;
+
+
+static void init_once(void *foo)
+{
+       struct squashfs_inode_info *ei = foo;
+
+       inode_init_once(&ei->vfs_inode);
+}
+
+
+static int __init init_inodecache(void)
+{
+       squashfs_inode_cachep = kmem_cache_create("squashfs_inode_cache",
+               sizeof(struct squashfs_inode_info), 0,
+               SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, init_once);
+
+       return squashfs_inode_cachep ? 0 : -ENOMEM;
+}
+
+
+static void destroy_inodecache(void)
+{
+       kmem_cache_destroy(squashfs_inode_cachep);
+}
+
+
+static int __init init_squashfs_fs(void)
+{
+       int err = init_inodecache();
+
+       if (err)
+               return err;
+
+       err = register_filesystem(&squashfs_fs_type);
+       if (err) {
+               destroy_inodecache();
+               return err;
+       }
+
+       printk(KERN_INFO "squashfs: version 4.0 (2009/01/03) "
+               "Phillip Lougher\n");
+
+       return 0;
+}
+
+
+static void __exit exit_squashfs_fs(void)
+{
+       unregister_filesystem(&squashfs_fs_type);
+       destroy_inodecache();
+}
+
+
+static struct inode *squashfs_alloc_inode(struct super_block *sb)
+{
+       struct squashfs_inode_info *ei =
+               kmem_cache_alloc(squashfs_inode_cachep, GFP_KERNEL);
+
+       return ei ? &ei->vfs_inode : NULL;
+}
+
+
+static void squashfs_destroy_inode(struct inode *inode)
+{
+       kmem_cache_free(squashfs_inode_cachep, squashfs_i(inode));
+}
+
+
+static struct file_system_type squashfs_fs_type = {
+       .owner = THIS_MODULE,
+       .name = "squashfs",
+       .get_sb = squashfs_get_sb,
+       .kill_sb = kill_block_super,
+       .fs_flags = FS_REQUIRES_DEV
+};
+
+static struct super_operations squashfs_super_ops = {
+       .alloc_inode = squashfs_alloc_inode,
+       .destroy_inode = squashfs_destroy_inode,
+       .statfs = squashfs_statfs,
+       .put_super = squashfs_put_super,
+       .remount_fs = squashfs_remount
+};
+
+module_init(init_squashfs_fs);
+module_exit(exit_squashfs_fs);
+MODULE_DESCRIPTION("squashfs 4.0, a compressed read-only filesystem");
+MODULE_AUTHOR("Phillip Lougher <phillip@lougher.demon.co.uk>");
+MODULE_LICENSE("GPL");
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/symlink.c b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/fs/squashfs/symlink.c
new file mode 100644 (file)
index 0000000..83d8788
--- /dev/null
@@ -0,0 +1,118 @@
+/*
+ * Squashfs - a compressed read only filesystem for Linux
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * symlink.c
+ */
+
+/*
+ * This file implements code to handle symbolic links.
+ *
+ * The data contents of symbolic links are stored inside the symbolic
+ * link inode within the inode table.  This allows the normally small symbolic
+ * link to be compressed as part of the inode table, achieving much greater
+ * compression than if the symbolic link was compressed individually.
+ */
+
+#include <linux/fs.h>
+#include <linux/vfs.h>
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/string.h>
+#include <linux/pagemap.h>
+#include <linux/zlib.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_fs_sb.h"
+#include "squashfs_fs_i.h"
+#include "squashfs.h"
+
+static int squashfs_symlink_readpage(struct file *file, struct page *page)
+{
+       struct inode *inode = page->mapping->host;
+       struct super_block *sb = inode->i_sb;
+       struct squashfs_sb_info *msblk = sb->s_fs_info;
+       int index = page->index << PAGE_CACHE_SHIFT;
+       u64 block = squashfs_i(inode)->start;
+       int offset = squashfs_i(inode)->offset;
+       int length = min_t(int, i_size_read(inode) - index, PAGE_CACHE_SIZE);
+       int bytes, copied;
+       void *pageaddr;
+       struct squashfs_cache_entry *entry;
+
+       TRACE("Entered squashfs_symlink_readpage, page index %ld, start block "
+                       "%llx, offset %x\n", page->index, block, offset);
+
+       /*
+        * Skip index bytes into symlink metadata.
+        */
+       if (index) {
+               bytes = squashfs_read_metadata(sb, NULL, &block, &offset,
+                                                               index);
+               if (bytes < 0) {
+                       ERROR("Unable to read symlink [%llx:%x]\n",
+                               squashfs_i(inode)->start,
+                               squashfs_i(inode)->offset);
+                       goto error_out;
+               }
+       }
+
+       /*
+        * Read length bytes from symlink metadata.  Squashfs_read_metadata
+        * is not used here because it can sleep and we want to use
+        * kmap_atomic to map the page.  Instead call the underlying
+        * squashfs_cache_get routine.  As length bytes may overlap metadata
+        * blocks, we may need to call squashfs_cache_get multiple times.
+        */
+       for (bytes = 0; bytes < length; offset = 0, bytes += copied) {
+               entry = squashfs_cache_get(sb, msblk->block_cache, block, 0);
+               if (entry->error) {
+                       ERROR("Unable to read symlink [%llx:%x]\n",
+                               squashfs_i(inode)->start,
+                               squashfs_i(inode)->offset);
+                       squashfs_cache_put(entry);
+                       goto error_out;
+               }
+
+               pageaddr = kmap_atomic(page, KM_USER0);
+               copied = squashfs_copy_data(pageaddr + bytes, entry, offset,
+                                                               length - bytes);
+               if (copied == length - bytes)
+                       memset(pageaddr + length, 0, PAGE_CACHE_SIZE - length);
+               else
+                       block = entry->next_index;
+               kunmap_atomic(pageaddr, KM_USER0);
+               squashfs_cache_put(entry);
+       }
+
+       flush_dcache_page(page);
+       SetPageUptodate(page);
+       unlock_page(page);
+       return 0;
+
+error_out:
+       SetPageError(page);
+       unlock_page(page);
+       return 0;
+}
+
+
+const struct address_space_operations squashfs_symlink_aops = {
+       .readpage = squashfs_symlink_readpage
+};
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/include/linux/squashfs_fs.h b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/include/linux/squashfs_fs.h
new file mode 100644 (file)
index 0000000..eef85b1
--- /dev/null
@@ -0,0 +1,380 @@
+#ifndef SQUASHFS_FS
+#define SQUASHFS_FS
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * squashfs_fs.h
+ */
+
+#define SQUASHFS_CACHED_FRAGMENTS      CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE
+#define SQUASHFS_MAJOR                 4
+#define SQUASHFS_MINOR                 0
+#define SQUASHFS_MAGIC                 0x73717368
+#define SQUASHFS_START                 0
+
+/* size of metadata (inode and directory) blocks */
+#define SQUASHFS_METADATA_SIZE         8192
+#define SQUASHFS_METADATA_LOG          13
+
+/* default size of data blocks */
+#define SQUASHFS_FILE_SIZE             131072
+#define SQUASHFS_FILE_LOG              17
+
+#define SQUASHFS_FILE_MAX_SIZE         1048576
+
+/* Max number of uids and gids */
+#define SQUASHFS_IDS                   65536
+
+/* Max length of filename (not 255) */
+#define SQUASHFS_NAME_LEN              256
+
+#define SQUASHFS_INVALID_FRAG          (0xffffffffU)
+#define SQUASHFS_INVALID_BLK           (-1LL)
+
+/* Filesystem flags */
+#define SQUASHFS_NOI                   0
+#define SQUASHFS_NOD                   1
+#define SQUASHFS_NOF                   3
+#define SQUASHFS_NO_FRAG               4
+#define SQUASHFS_ALWAYS_FRAG           5
+#define SQUASHFS_DUPLICATE             6
+#define SQUASHFS_EXPORT                        7
+
+#define SQUASHFS_BIT(flag, bit)                ((flag >> bit) & 1)
+
+#define SQUASHFS_UNCOMPRESSED_INODES(flags)    SQUASHFS_BIT(flags, \
+                                               SQUASHFS_NOI)
+
+#define SQUASHFS_UNCOMPRESSED_DATA(flags)      SQUASHFS_BIT(flags, \
+                                               SQUASHFS_NOD)
+
+#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
+                                               SQUASHFS_NOF)
+
+#define SQUASHFS_NO_FRAGMENTS(flags)           SQUASHFS_BIT(flags, \
+                                               SQUASHFS_NO_FRAG)
+
+#define SQUASHFS_ALWAYS_FRAGMENTS(flags)       SQUASHFS_BIT(flags, \
+                                               SQUASHFS_ALWAYS_FRAG)
+
+#define SQUASHFS_DUPLICATES(flags)             SQUASHFS_BIT(flags, \
+                                               SQUASHFS_DUPLICATE)
+
+#define SQUASHFS_EXPORTABLE(flags)             SQUASHFS_BIT(flags, \
+                                               SQUASHFS_EXPORT)
+
+/* Max number of types and file types */
+#define SQUASHFS_DIR_TYPE              1
+#define SQUASHFS_REG_TYPE              2
+#define SQUASHFS_SYMLINK_TYPE          3
+#define SQUASHFS_BLKDEV_TYPE           4
+#define SQUASHFS_CHRDEV_TYPE           5
+#define SQUASHFS_FIFO_TYPE             6
+#define SQUASHFS_SOCKET_TYPE           7
+#define SQUASHFS_LDIR_TYPE             8
+#define SQUASHFS_LREG_TYPE             9
+#define SQUASHFS_LSYMLINK_TYPE         10
+#define SQUASHFS_LBLKDEV_TYPE          11
+#define SQUASHFS_LCHRDEV_TYPE          12
+#define SQUASHFS_LFIFO_TYPE            13
+#define SQUASHFS_LSOCKET_TYPE          14
+
+/* Flag whether block is compressed or uncompressed, bit is set if block is
+ * uncompressed */
+#define SQUASHFS_COMPRESSED_BIT                (1 << 15)
+
+#define SQUASHFS_COMPRESSED_SIZE(B)    (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
+               (B) & ~SQUASHFS_COMPRESSED_BIT :  SQUASHFS_COMPRESSED_BIT)
+
+#define SQUASHFS_COMPRESSED(B)         (!((B) & SQUASHFS_COMPRESSED_BIT))
+
+#define SQUASHFS_COMPRESSED_BIT_BLOCK  (1 << 24)
+
+#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B)      ((B) & \
+                                               ~SQUASHFS_COMPRESSED_BIT_BLOCK)
+
+#define SQUASHFS_COMPRESSED_BLOCK(B)   (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
+
+/*
+ * Inode number ops.  Inodes consist of a compressed block number, and an
+ * uncompressed offset within that block
+ */
+#define SQUASHFS_INODE_BLK(A)          ((unsigned int) ((A) >> 16))
+
+#define SQUASHFS_INODE_OFFSET(A)       ((unsigned int) ((A) & 0xffff))
+
+#define SQUASHFS_MKINODE(A, B)         ((long long)(((long long) (A)\
+                                       << 16) + (B)))
+
+/* Translate between VFS mode and squashfs mode */
+#define SQUASHFS_MODE(A)               ((A) & 0xfff)
+
+/* fragment and fragment table defines */
+#define SQUASHFS_FRAGMENT_BYTES(A)     \
+                               ((A) * sizeof(struct squashfs_fragment_entry))
+
+#define SQUASHFS_FRAGMENT_INDEX(A)     (SQUASHFS_FRAGMENT_BYTES(A) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A)      (SQUASHFS_FRAGMENT_BYTES(A) % \
+                                               SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEXES(A)   ((SQUASHFS_FRAGMENT_BYTES(A) + \
+                                       SQUASHFS_METADATA_SIZE - 1) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_BYTES(A)       (SQUASHFS_FRAGMENT_INDEXES(A) *\
+                                               sizeof(long long))
+
+/* inode lookup table defines */
+#define SQUASHFS_LOOKUP_BYTES(A)       ((A) * sizeof(long long))
+
+#define SQUASHFS_LOOKUP_BLOCK(A)       (SQUASHFS_LOOKUP_BYTES(A) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A)        (SQUASHFS_LOOKUP_BYTES(A) % \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_LOOKUP_BLOCKS(A)      ((SQUASHFS_LOOKUP_BYTES(A) + \
+                                       SQUASHFS_METADATA_SIZE - 1) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_LOOKUP_BLOCK_BYTES(A) (SQUASHFS_LOOKUP_BLOCKS(A) *\
+                                       sizeof(long long))
+
+/* uid/gid lookup table defines */
+#define SQUASHFS_ID_BYTES(A)           ((A) * sizeof(unsigned int))
+
+#define SQUASHFS_ID_BLOCK(A)           (SQUASHFS_ID_BYTES(A) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_ID_BLOCK_OFFSET(A)    (SQUASHFS_ID_BYTES(A) % \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_ID_BLOCKS(A)          ((SQUASHFS_ID_BYTES(A) + \
+                                       SQUASHFS_METADATA_SIZE - 1) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_ID_BLOCK_BYTES(A)     (SQUASHFS_ID_BLOCKS(A) *\
+                                       sizeof(long long))
+
+/* cached data constants for filesystem */
+#define SQUASHFS_CACHED_BLKS           8
+
+#define SQUASHFS_MAX_FILE_SIZE_LOG     64
+
+#define SQUASHFS_MAX_FILE_SIZE         (1LL << \
+                                       (SQUASHFS_MAX_FILE_SIZE_LOG - 2))
+
+#define SQUASHFS_MARKER_BYTE           0xff
+
+/* meta index cache */
+#define SQUASHFS_META_INDEXES  (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
+#define SQUASHFS_META_ENTRIES  127
+#define SQUASHFS_META_SLOTS    8
+
+struct meta_entry {
+       long long               data_block;
+       unsigned int            index_block;
+       unsigned short          offset;
+       unsigned short          pad;
+};
+
+struct meta_index {
+       unsigned int            inode_number;
+       unsigned int            offset;
+       unsigned short          entries;
+       unsigned short          skip;
+       unsigned short          locked;
+       unsigned short          pad;
+       struct meta_entry       meta_entry[SQUASHFS_META_ENTRIES];
+};
+
+
+/*
+ * definitions for structures on disk
+ */
+#define ZLIB_COMPRESSION        1
+
+struct squashfs_super_block {
+       __le32                  s_magic;
+       __le32                  inodes;
+       __le32                  mkfs_time;
+       __le32                  block_size;
+       __le32                  fragments;
+       __le16                  compression;
+       __le16                  block_log;
+       __le16                  flags;
+       __le16                  no_ids;
+       __le16                  s_major;
+       __le16                  s_minor;
+       __le64                  root_inode;
+       __le64                  bytes_used;
+       __le64                  id_table_start;
+       __le64                  xattr_table_start;
+       __le64                  inode_table_start;
+       __le64                  directory_table_start;
+       __le64                  fragment_table_start;
+       __le64                  lookup_table_start;
+};
+
+struct squashfs_dir_index {
+       __le32                  index;
+       __le32                  start_block;
+       __le32                  size;
+       unsigned char           name[0];
+};
+
+struct squashfs_base_inode {
+       __le16                  inode_type;
+       __le16                  mode;
+       __le16                  uid;
+       __le16                  guid;
+       __le32                  mtime;
+       __le32                  inode_number;
+};
+
+struct squashfs_ipc_inode {
+       __le16                  inode_type;
+       __le16                  mode;
+       __le16                  uid;
+       __le16                  guid;
+       __le32                  mtime;
+       __le32                  inode_number;
+       __le32                  nlink;
+};
+
+struct squashfs_dev_inode {
+       __le16                  inode_type;
+       __le16                  mode;
+       __le16                  uid;
+       __le16                  guid;
+       __le32                  mtime;
+       __le32                  inode_number;
+       __le32                  nlink;
+       __le32                  rdev;
+};
+
+struct squashfs_symlink_inode {
+       __le16                  inode_type;
+       __le16                  mode;
+       __le16                  uid;
+       __le16                  guid;
+       __le32                  mtime;
+       __le32                  inode_number;
+       __le32                  nlink;
+       __le32                  symlink_size;
+       char                    symlink[0];
+};
+
+struct squashfs_reg_inode {
+       __le16                  inode_type;
+       __le16                  mode;
+       __le16                  uid;
+       __le16                  guid;
+       __le32                  mtime;
+       __le32                  inode_number;
+       __le32                  start_block;
+       __le32                  fragment;
+       __le32                  offset;
+       __le32                  file_size;
+       __le16                  block_list[0];
+};
+
+struct squashfs_lreg_inode {
+       __le16                  inode_type;
+       __le16                  mode;
+       __le16                  uid;
+       __le16                  guid;
+       __le32                  mtime;
+       __le32                  inode_number;
+       __le64                  start_block;
+       __le64                  file_size;
+       __le64                  sparse;
+       __le32                  nlink;
+       __le32                  fragment;
+       __le32                  offset;
+       __le32                  xattr;
+       __le16                  block_list[0];
+};
+
+struct squashfs_dir_inode {
+       __le16                  inode_type;
+       __le16                  mode;
+       __le16                  uid;
+       __le16                  guid;
+       __le32                  mtime;
+       __le32                  inode_number;
+       __le32                  start_block;
+       __le32                  nlink;
+       __le16                  file_size;
+       __le16                  offset;
+       __le32                  parent_inode;
+};
+
+struct squashfs_ldir_inode {
+       __le16                  inode_type;
+       __le16                  mode;
+       __le16                  uid;
+       __le16                  guid;
+       __le32                  mtime;
+       __le32                  inode_number;
+       __le32                  nlink;
+       __le32                  file_size;
+       __le32                  start_block;
+       __le32                  parent_inode;
+       __le16                  i_count;
+       __le16                  offset;
+       __le32                  xattr;
+       struct squashfs_dir_index       index[0];
+};
+
+union squashfs_inode {
+       struct squashfs_base_inode              base;
+       struct squashfs_dev_inode               dev;
+       struct squashfs_symlink_inode           symlink;
+       struct squashfs_reg_inode               reg;
+       struct squashfs_lreg_inode              lreg;
+       struct squashfs_dir_inode               dir;
+       struct squashfs_ldir_inode              ldir;
+       struct squashfs_ipc_inode               ipc;
+};
+
+struct squashfs_dir_entry {
+       __le16                  offset;
+       __le16                  inode_number;
+       __le16                  type;
+       __le16                  size;
+       char                    name[0];
+};
+
+struct squashfs_dir_header {
+       __le32                  count;
+       __le32                  start_block;
+       __le32                  inode_number;
+};
+
+struct squashfs_fragment_entry {
+       __le64                  start_block;
+       __le32                  size;
+       unsigned int            unused;
+};
+
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/include/linux/squashfs_fs_i.h b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/include/linux/squashfs_fs_i.h
new file mode 100644 (file)
index 0000000..f78e6f8
--- /dev/null
@@ -0,0 +1,45 @@
+#ifndef SQUASHFS_FS_I
+#define SQUASHFS_FS_I
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * squashfs_fs_i.h
+ */
+
+struct squashfs_inode_info {
+       long long       start;
+       int             offset;
+       union {
+               struct {
+                       long long       fragment_block;
+                       int             fragment_size;
+                       int             fragment_offset;
+                       long long       block_list_start;
+               };
+               struct {
+                       long long       dir_idx_start;
+                       int             dir_idx_offset;
+                       int             dir_idx_cnt;
+                       int             parent;
+               };
+       };
+       struct inode    vfs_inode;
+};
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/include/linux/squashfs_fs_sb.h b/SQUASHFS/squashfs-tools-4.4/kernel/kernel-2.6/include/linux/squashfs_fs_sb.h
new file mode 100644 (file)
index 0000000..cc9fd5d
--- /dev/null
@@ -0,0 +1,76 @@
+#ifndef SQUASHFS_FS_SB
+#define SQUASHFS_FS_SB
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+ * Phillip Lougher <phillip@lougher.demon.co.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * squashfs_fs_sb.h
+ */
+
+#include "squashfs_fs.h"
+
+struct squashfs_cache_entry {
+       long long               block;
+       int                     length;
+       int                     locked;
+       long long               next_index;
+       char                    pending;
+       char                    error;
+       int                     waiting;
+       wait_queue_head_t       wait_queue;
+       char                    *data;
+};
+
+struct squashfs_cache {
+       char                    *name;
+       int                     entries;
+       int                     block_size;
+       int                     next_blk;
+       int                     waiting;
+       int                     unused;
+       int                     use_vmalloc;
+       spinlock_t              lock;
+       wait_queue_head_t       wait_queue;
+       struct squashfs_cache_entry entry[0];
+};
+
+struct squashfs_sb_info {
+       int                     devblksize;
+       int                     devblksize_log2;
+       struct squashfs_cache   *block_cache;
+       struct squashfs_cache   *fragment_cache;
+       int                     next_meta_index;
+       __le64                  *id_table;
+       __le64                  *fragment_index;
+       unsigned int            *fragment_index_2;
+       char                    *read_page;
+       struct mutex            read_data_mutex;
+       struct mutex            read_page_mutex;
+       struct mutex            meta_index_mutex;
+       struct meta_index       *meta_index;
+       z_stream                stream;
+       __le64                  *inode_lookup_table;
+       long long               inode_table;
+       long long               directory_table;
+       unsigned int            block_size;
+       unsigned short          block_log;
+       long long               bytes_used;
+       unsigned int            inodes;
+};
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/Makefile b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/Makefile
new file mode 100644 (file)
index 0000000..a772201
--- /dev/null
@@ -0,0 +1,365 @@
+###############################################
+#          Compression build options          #
+###############################################
+#
+#
+############# Building gzip support ###########
+#
+# Gzip support is by default enabled, and the compression type default
+# (COMP_DEFAULT) is gzip.
+#
+# If you don't want/need gzip support then comment out the GZIP SUPPORT line
+# below, and change COMP_DEFAULT to one of the compression types you have
+# selected.
+#
+# Obviously, you must select at least one of the available gzip, lzma, lzo
+# compression types.
+#
+GZIP_SUPPORT = 1
+
+########### Building XZ support #############
+#
+# LZMA2 compression.
+#
+# XZ Utils liblzma (http://tukaani.org/xz/) is supported
+#
+# Development packages (libraries and header files) should be
+# supported by most modern distributions.  Please refer to
+# your distribution package manager.
+#
+# To build install the library and uncomment
+# the XZ_SUPPORT line below.
+#
+XZ_SUPPORT = 1
+
+
+############ Building LZO support ##############
+#
+# The LZO library (http://www.oberhumer.com/opensource/lzo/) is supported.
+#
+# Development packages (libraries and header files) should be
+# supported by most modern distributions.  Please refer to
+# your distribution package manager.
+#
+# To build install the library and uncomment
+# the XZ_SUPPORT line below.
+#
+LZO_SUPPORT = 1
+
+
+########### Building LZ4 support #############
+#
+# Yann Collet's LZ4 tools are supported
+# LZ4 homepage: http://fastcompression.blogspot.com/p/lz4.html
+# LZ4 source repository: http://code.google.com/p/lz4
+#
+# Development packages (libraries and header files) should be
+# supported by most modern distributions.  Please refer to
+# your distribution package manager.
+#
+# To build install and uncomment
+# the LZ4_SUPPORT line below.
+#
+LZ4_SUPPORT = 1
+
+
+########### Building ZSTD support ############
+#
+# The ZSTD library is supported
+# ZSTD homepage: http://zstd.net
+# ZSTD source repository: https://github.com/facebook/zstd
+#
+# Development packages (libraries and header files) should be
+# supported by most modern distributions.  Please refer to
+# your distribution package manager.
+#
+# To build install the library and uncomment
+# the XZ_SUPPORT line below.
+#
+ZSTD_SUPPORT = 1
+
+
+######## Specifying default compression ########
+#
+# The next line specifies which compression algorithm is used by default
+# in Mksquashfs.  Obviously the compression algorithm must have been
+# selected to be built
+#
+COMP_DEFAULT = gzip
+
+
+###############################################
+#  Extended attribute (XATTRs) build options  #
+###############################################
+#
+# Building XATTR support for Mksquashfs and Unsquashfs
+#
+# If your C library or build/target environment doesn't support XATTRs then
+# comment out the next line to build Mksquashfs and Unsquashfs without XATTR
+# support
+XATTR_SUPPORT = 1
+
+# Select whether you wish xattrs to be stored by Mksquashfs and extracted
+# by Unsquashfs by default.  If selected users can disable xattr support by
+# using the -no-xattrs option
+#
+# If unselected, Mksquashfs/Unsquashfs won't store and extract xattrs by
+# default.  Users can enable xattrs by using the -xattrs option.
+XATTR_DEFAULT = 1
+
+
+###############################################
+#          Reproducible Image options         #
+###############################################
+#
+# Select whether you wish reproducible builds by default.  If selected users
+# can disable reproducible builds using the not-reproducible option.
+# If not selected, users can enable reproducible builds using the
+# -reproducible option
+REPRODUCIBLE_DEFAULT = 1
+
+
+###############################################
+#              Obsolete options               #
+###############################################
+
+########### Building LZMA support #############
+#
+# LZMA1 compression.
+#
+# LZMA1 compression is obsolete, and the newer and better XZ (LZMA2)
+# compression should be used in preference.
+#
+# Both XZ Utils liblzma (http://tukaani.org/xz/) and LZMA SDK
+# (http://www.7-zip.org/sdk.html) are supported
+#
+# To build using XZ Utils liblzma - install the library and uncomment
+# the LZMA_XZ_SUPPORT line below.
+#
+# To build using the LZMA SDK (4.65 used in development, other versions may
+# work) - download and unpack it, uncomment and set LZMA_DIR to unpacked source,
+# and uncomment the LZMA_SUPPORT line below.
+#
+LZMA_XZ_SUPPORT = 1
+#LZMA_SUPPORT = 1
+#LZMA_DIR = ../../../../LZMA/lzma465
+
+###############################################
+#        End of BUILD options section         #
+###############################################
+
+INCLUDEDIR = -I.
+INSTALL_DIR = /usr/local/bin
+
+MKSQUASHFS_OBJS = mksquashfs.o read_fs.o action.o swap.o pseudo.o compressor.o \
+       sort.o progressbar.o read_file.o info.o restore.o process_fragments.o \
+       caches-queues-lists.o
+
+UNSQUASHFS_OBJS = unsquashfs.o unsquash-1.o unsquash-2.o unsquash-3.o \
+       unsquash-4.o unsquash-123.o unsquash-34.o swap.o compressor.o unsquashfs_info.o
+
+CFLAGS ?= -Os
+CFLAGS += $(EXTRA_CFLAGS) $(INCLUDEDIR) -D_FILE_OFFSET_BITS=64 \
+       -D_LARGEFILE_SOURCE -D_GNU_SOURCE -DCOMP_DEFAULT=\"$(COMP_DEFAULT)\" \
+       -Wall
+
+LIBS = -lpthread
+ifeq ($(GZIP_SUPPORT),1)
+CFLAGS += -DGZIP_SUPPORT
+MKSQUASHFS_OBJS += gzip_wrapper.o
+UNSQUASHFS_OBJS += gzip_wrapper.o
+LIBS += $(VTZLIB)
+COMPRESSORS += gzip
+endif
+
+ifeq ($(LZMA_SUPPORT),1)
+LZMA_OBJS = $(LZMA_DIR)/C/Alloc.o $(LZMA_DIR)/C/LzFind.o \
+       $(LZMA_DIR)/C/LzmaDec.o $(LZMA_DIR)/C/LzmaEnc.o $(LZMA_DIR)/C/LzmaLib.o
+INCLUDEDIR += -I$(LZMA_DIR)/C
+CFLAGS += -DLZMA_SUPPORT
+MKSQUASHFS_OBJS += lzma_wrapper.o $(LZMA_OBJS)
+UNSQUASHFS_OBJS += lzma_wrapper.o $(LZMA_OBJS)
+COMPRESSORS += lzma
+endif
+
+ifeq ($(LZMA_XZ_SUPPORT),1)
+CFLAGS += -DLZMA_SUPPORT
+MKSQUASHFS_OBJS += lzma_xz_wrapper.o
+UNSQUASHFS_OBJS += lzma_xz_wrapper.o
+LIBS += 
+COMPRESSORS += lzma
+endif
+
+ifeq ($(XZ_SUPPORT),1)
+CFLAGS += -DXZ_SUPPORT -I$(LZMA_LIBDIR)/include
+MKSQUASHFS_OBJS += xz_wrapper.o
+UNSQUASHFS_OBJS += xz_wrapper.o
+LIBS += $(LZMA_LIBDIR)/lib/liblzma.a
+COMPRESSORS += xz
+endif
+
+ifeq ($(LZO_SUPPORT),1)
+CFLAGS += -DLZO_SUPPORT -I $(LZO_LIBDIR)/include
+MKSQUASHFS_OBJS += lzo_wrapper.o
+UNSQUASHFS_OBJS += lzo_wrapper.o
+LIBS += $(LZO_LIBDIR)/lib/liblzo2.a
+COMPRESSORS += lzo
+endif
+
+ifeq ($(LZ4_SUPPORT),1)
+CFLAGS += -DLZ4_SUPPORT -I $(LZ4_LIBDIR)/include
+MKSQUASHFS_OBJS += lz4_wrapper.o
+UNSQUASHFS_OBJS += lz4_wrapper.o
+LIBS += $(LZ4_LIBDIR)/lib/liblz4.a
+COMPRESSORS += lz4
+endif
+
+ifeq ($(ZSTD_SUPPORT),1)
+CFLAGS += -DZSTD_SUPPORT  -I $(ZSTD_LIBDIR)/include
+MKSQUASHFS_OBJS += zstd_wrapper.o
+UNSQUASHFS_OBJS += zstd_wrapper.o
+LIBS += $(ZSTD_LIBDIR)/lib/libzstd.a
+COMPRESSORS += zstd
+endif
+
+ifeq ($(XATTR_SUPPORT),1)
+ifeq ($(XATTR_DEFAULT),1)
+CFLAGS += -DXATTR_SUPPORT -DXATTR_DEFAULT
+else
+CFLAGS += -DXATTR_SUPPORT
+endif
+MKSQUASHFS_OBJS += xattr.o read_xattrs.o
+UNSQUASHFS_OBJS += read_xattrs.o unsquashfs_xattr.o
+endif
+
+ifeq ($(REPRODUCIBLE_DEFAULT),1)
+CFLAGS += -DREPRODUCIBLE_DEFAULT
+endif
+
+#
+# If LZMA_SUPPORT is specified then LZMA_DIR must be specified too
+#
+ifeq ($(LZMA_SUPPORT),1)
+ifndef LZMA_DIR
+$(error "LZMA_SUPPORT requires LZMA_DIR to be also defined")
+endif
+endif
+
+#
+# Both LZMA_XZ_SUPPORT and LZMA_SUPPORT cannot be specified
+#
+ifeq ($(LZMA_XZ_SUPPORT),1)
+ifeq ($(LZMA_SUPPORT),1)
+$(error "Both LZMA_XZ_SUPPORT and LZMA_SUPPORT cannot be specified")
+endif
+endif
+
+#
+# At least one compressor must have been selected
+#
+ifndef COMPRESSORS
+$(error "No compressor selected! Select one or more of GZIP, LZMA, XZ, LZO, \
+       LZ4 or ZSTD!")
+endif
+
+#
+# COMP_DEFAULT should be defined
+#
+ifndef COMP_DEFAULT
+$(error "COMP_DEFAULT must be set to a compressor!")
+endif
+
+#
+# COMP_DEFAULT must be a selected compressor
+#
+ifeq (, $(findstring $(COMP_DEFAULT), $(COMPRESSORS)))
+$(error "COMP_DEFAULT is set to ${COMP_DEFAULT}, which  isn't selected to be \
+       built!")
+endif
+
+.PHONY: all
+all: mksquashfs unsquashfs
+
+mksquashfs: $(MKSQUASHFS_OBJS)
+       $(CC) $(LDFLAGS) $(EXTRA_LDFLAGS) $(MKSQUASHFS_OBJS) $(LIBS) -o $@
+
+mksquashfs.o: Makefile mksquashfs.c squashfs_fs.h squashfs_swap.h mksquashfs.h \
+       sort.h pseudo.h compressor.h xattr.h action.h error.h progressbar.h \
+       info.h caches-queues-lists.h read_fs.h restore.h process_fragments.h 
+
+read_fs.o: read_fs.c squashfs_fs.h squashfs_swap.h compressor.h xattr.h \
+       error.h mksquashfs.h
+
+sort.o: sort.c squashfs_fs.h mksquashfs.h sort.h error.h progressbar.h
+
+swap.o: swap.c
+
+pseudo.o: pseudo.c pseudo.h error.h progressbar.h
+
+compressor.o: Makefile compressor.c compressor.h squashfs_fs.h
+
+xattr.o: xattr.c squashfs_fs.h squashfs_swap.h mksquashfs.h xattr.h error.h \
+       progressbar.h
+
+read_xattrs.o: read_xattrs.c squashfs_fs.h squashfs_swap.h xattr.h error.h
+
+action.o: action.c squashfs_fs.h mksquashfs.h action.h error.h
+
+progressbar.o: progressbar.c error.h
+
+read_file.o: read_file.c error.h
+
+info.o: info.c squashfs_fs.h mksquashfs.h error.h progressbar.h \
+       caches-queues-lists.h
+
+restore.o: restore.c caches-queues-lists.h squashfs_fs.h mksquashfs.h error.h \
+       progressbar.h info.h
+
+process_fragments.o: process_fragments.c process_fragments.h
+
+caches-queues-lists.o: caches-queues-lists.c error.h caches-queues-lists.h
+
+gzip_wrapper.o: gzip_wrapper.c squashfs_fs.h gzip_wrapper.h compressor.h
+
+lzma_wrapper.o: lzma_wrapper.c compressor.h squashfs_fs.h
+
+lzma_xz_wrapper.o: lzma_xz_wrapper.c compressor.h squashfs_fs.h
+
+lzo_wrapper.o: lzo_wrapper.c squashfs_fs.h lzo_wrapper.h compressor.h
+
+lz4_wrapper.o: lz4_wrapper.c squashfs_fs.h lz4_wrapper.h compressor.h
+
+xz_wrapper.o: xz_wrapper.c squashfs_fs.h xz_wrapper.h compressor.h
+
+unsquashfs: $(UNSQUASHFS_OBJS)
+       $(CC) $(LDFLAGS) $(EXTRA_LDFLAGS) $(UNSQUASHFS_OBJS) $(LIBS) -o $@
+
+unsquashfs.o: unsquashfs.h unsquashfs.c squashfs_fs.h squashfs_swap.h \
+       squashfs_compat.h xattr.h read_fs.h compressor.h
+
+unsquash-1.o: unsquashfs.h unsquash-1.c squashfs_fs.h squashfs_compat.h
+
+unsquash-2.o: unsquashfs.h unsquash-2.c squashfs_fs.h squashfs_compat.h
+
+unsquash-3.o: unsquashfs.h unsquash-3.c squashfs_fs.h squashfs_compat.h
+
+unsquash-4.o: unsquashfs.h unsquash-4.c squashfs_fs.h squashfs_swap.h \
+       read_fs.h
+
+unsquash-123.o: unsquashfs.h unsquash-123.c squashfs_fs.h squashfs_compat.h
+
+unsquash-34.o: unsquashfs.h unsquash-34.c
+
+unsquashfs_xattr.o: unsquashfs_xattr.c unsquashfs.h squashfs_fs.h xattr.h
+
+unsquashfs_info.o: unsquashfs.h squashfs_fs.h
+
+.PHONY: clean
+clean:
+       -rm -f *.o mksquashfs unsquashfs
+
+.PHONY: install
+install: mksquashfs unsquashfs
+       mkdir -p $(INSTALL_DIR)
+       cp mksquashfs $(INSTALL_DIR)
+       cp unsquashfs $(INSTALL_DIR)
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/action.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/action.c
new file mode 100644 (file)
index 0000000..4b06ccb
--- /dev/null
@@ -0,0 +1,3266 @@
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2011, 2012, 2013, 2014
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * action.c
+ */
+
+#include <fcntl.h>
+#include <dirent.h>
+#include <stddef.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <pwd.h>
+#include <grp.h>
+#include <sys/wait.h>
+#include <regex.h>
+#include <limits.h>
+#include <errno.h>
+
+#include "squashfs_fs.h"
+#include "mksquashfs.h"
+#include "action.h"
+#include "error.h"
+#include "fnmatch_compat.h"
+
+/*
+ * code to parse actions
+ */
+
+static char *cur_ptr, *source;
+static struct action *fragment_spec = NULL;
+static struct action *exclude_spec = NULL;
+static struct action *empty_spec = NULL;
+static struct action *move_spec = NULL;
+static struct action *prune_spec = NULL;
+static struct action *other_spec = NULL;
+static int fragment_count = 0;
+static int exclude_count = 0;
+static int empty_count = 0;
+static int move_count = 0;
+static int prune_count = 0;
+static int other_count = 0;
+static struct action_entry *parsing_action;
+
+static struct file_buffer *def_fragment = NULL;
+
+static struct token_entry token_table[] = {
+       { "(", TOK_OPEN_BRACKET, 1, },
+       { ")", TOK_CLOSE_BRACKET, 1 },
+       { "&&", TOK_AND, 2 },
+       { "||", TOK_OR, 2 },
+       { "!", TOK_NOT, 1 },
+       { ",", TOK_COMMA, 1 },
+       { "@", TOK_AT, 1},
+       { " ",  TOK_WHITE_SPACE, 1 },
+       { "\t ", TOK_WHITE_SPACE, 1 },
+       { "", -1, 0 }
+};
+
+
+static struct test_entry test_table[];
+
+static struct action_entry action_table[];
+
+static struct expr *parse_expr(int subexp);
+
+extern char *pathname(struct dir_ent *);
+
+extern char *subpathname(struct dir_ent *);
+
+extern int read_file(char *filename, char *type, int (parse_line)(char *));
+
+/*
+ * Lexical analyser
+ */
+#define STR_SIZE 256
+
+static int get_token(char **string)
+{
+       /* string buffer */
+       static char *str = NULL;
+       static int size = 0;
+
+       char *str_ptr;
+       int cur_size, i, quoted;
+
+       while (1) {
+               if (*cur_ptr == '\0')
+                       return TOK_EOF;
+               for (i = 0; token_table[i].token != -1; i++)
+                       if (strncmp(cur_ptr, token_table[i].string,
+                                               token_table[i].size) == 0)
+                               break;
+               if (token_table[i].token != TOK_WHITE_SPACE)
+                       break;
+               cur_ptr ++;
+       }
+
+       if (token_table[i].token != -1) {
+               cur_ptr += token_table[i].size;
+               return token_table[i].token;
+       }
+
+       /* string */
+       if(str == NULL) {
+               str = malloc(STR_SIZE);
+               if(str == NULL)
+                       MEM_ERROR();
+               size = STR_SIZE;
+       }
+
+       /* Initialise string being read */
+       str_ptr = str;
+       cur_size = 0;
+       quoted = 0;
+
+       while(1) {
+               while(*cur_ptr == '"') {
+                       cur_ptr ++;
+                       quoted = !quoted;
+               }
+
+               if(*cur_ptr == '\0') {
+                       /* inside quoted string EOF, otherwise end of string */
+                       if(quoted)
+                               return TOK_EOF;
+                       else
+                               break;
+               }
+
+               if(!quoted) {
+                       for(i = 0; token_table[i].token != -1; i++)
+                               if (strncmp(cur_ptr, token_table[i].string,
+                                               token_table[i].size) == 0)
+                                       break;
+                       if (token_table[i].token != -1)
+                               break;
+               }
+
+               if(*cur_ptr == '\\') {
+                       cur_ptr ++;
+                       if(*cur_ptr == '\0')
+                               return TOK_EOF;
+               }
+
+               if(cur_size + 2 > size) {
+                       char *tmp;
+
+                       size = (cur_size + 1  + STR_SIZE) & ~(STR_SIZE - 1);
+
+                       tmp = realloc(str, size);
+                       if(tmp == NULL)
+                               MEM_ERROR();
+
+                       str_ptr = str_ptr - str + tmp;
+                       str = tmp;
+               }
+
+               *str_ptr ++ = *cur_ptr ++;
+               cur_size ++;
+       }
+
+       *str_ptr = '\0';
+       *string = str;
+       return TOK_STRING;
+}
+
+
+static int peek_token(char **string)
+{
+       char *saved = cur_ptr;
+       int token = get_token(string);
+
+       cur_ptr = saved;
+
+       return token;
+}
+
+
+/*
+ * Expression parser
+ */
+static void free_parse_tree(struct expr *expr)
+{
+       if(expr->type == ATOM_TYPE) {
+               int i;
+
+               for(i = 0; i < expr->atom.test->args; i++)
+                       free(expr->atom.argv[i]);
+
+               free(expr->atom.argv);
+       } else if (expr->type == UNARY_TYPE)
+               free_parse_tree(expr->unary_op.expr);
+       else {
+               free_parse_tree(expr->expr_op.lhs);
+               free_parse_tree(expr->expr_op.rhs);
+       }
+
+       free(expr);
+}
+
+
+static struct expr *create_expr(struct expr *lhs, int op, struct expr *rhs)
+{
+       struct expr *expr;
+
+       if (rhs == NULL) {
+               free_parse_tree(lhs);
+               return NULL;
+       }
+
+       expr = malloc(sizeof(*expr));
+       if (expr == NULL)
+               MEM_ERROR();
+
+       expr->type = OP_TYPE;
+       expr->expr_op.lhs = lhs;
+       expr->expr_op.rhs = rhs;
+       expr->expr_op.op = op;
+
+       return expr;
+}
+
+
+static struct expr *create_unary_op(struct expr *lhs, int op)
+{
+       struct expr *expr;
+
+       if (lhs == NULL)
+               return NULL;
+
+       expr = malloc(sizeof(*expr));
+       if (expr == NULL)
+               MEM_ERROR();
+
+       expr->type = UNARY_TYPE;
+       expr->unary_op.expr = lhs;
+       expr->unary_op.op = op;
+
+       return expr;
+}
+
+
+static struct expr *parse_test(char *name)
+{
+       char *string, **argv = NULL;
+       int token, args = 0;
+       int i;
+       struct test_entry *test;
+       struct expr *expr;
+
+       for (i = 0; test_table[i].args != -1; i++)
+               if (strcmp(name, test_table[i].name) == 0)
+                       break;
+
+       test = &test_table[i];
+
+       if (test->args == -1) {
+               SYNTAX_ERROR("Non-existent test \"%s\"\n", name);
+               return NULL;
+       }
+
+       if(parsing_action->type == EXCLUDE_ACTION && !test->exclude_ok) {
+               fprintf(stderr, "Failed to parse action \"%s\"\n", source);
+               fprintf(stderr, "Test \"%s\" cannot be used in exclude "
+                                                       "actions\n", name);
+               fprintf(stderr, "Use prune action instead ...\n");
+               return NULL;
+       }
+
+       expr = malloc(sizeof(*expr));
+       if (expr == NULL)
+               MEM_ERROR();
+
+       expr->type = ATOM_TYPE;
+
+       expr->atom.test = test;
+       expr->atom.data = NULL;
+
+       /*
+        * If the test has no arguments, then go straight to checking if there's
+        * enough arguments
+        */
+       token = peek_token(&string);
+
+       if (token != TOK_OPEN_BRACKET)
+                       goto skip_args;
+
+       get_token(&string);
+
+       /*
+        * speculatively read all the arguments, and then see if the
+        * number of arguments read is the number expected, this handles
+        * tests with a variable number of arguments
+        */
+       token = get_token(&string);
+       if (token == TOK_CLOSE_BRACKET)
+               goto skip_args;
+
+       while(1) {
+               if (token != TOK_STRING) {
+                       SYNTAX_ERROR("Unexpected token \"%s\", expected "
+                               "argument\n", TOK_TO_STR(token, string));
+                       goto failed;
+               }
+
+               argv = realloc(argv, (args + 1) * sizeof(char *));
+               if (argv == NULL)
+                       MEM_ERROR();
+
+               argv[args ++ ] = strdup(string);
+
+               token = get_token(&string);
+
+               if (token == TOK_CLOSE_BRACKET)
+                       break;
+
+               if (token != TOK_COMMA) {
+                       SYNTAX_ERROR("Unexpected token \"%s\", expected "
+                               "\",\" or \")\"\n", TOK_TO_STR(token, string));
+                       goto failed;
+               }
+               token = get_token(&string);
+       }
+
+skip_args:
+       /*
+        * expected number of arguments?
+        */
+       if(test->args != -2 && args != test->args) {
+               SYNTAX_ERROR("Unexpected number of arguments, expected %d, "
+                       "got %d\n", test->args, args);
+               goto failed;
+       }
+
+       expr->atom.args = args;
+       expr->atom.argv = argv;
+
+       if (test->parse_args) {
+               int res = test->parse_args(test, &expr->atom);
+
+               if (res == 0)           
+                       goto failed;
+       }
+
+       return expr;
+
+failed:
+       free(argv);
+       free(expr);
+       return NULL;
+}
+
+
+static struct expr *get_atom()
+{
+       char *string;
+       int token = get_token(&string);
+
+       switch(token) {
+       case TOK_NOT:
+               return create_unary_op(get_atom(), token);
+       case TOK_OPEN_BRACKET:
+               return parse_expr(1);
+       case TOK_STRING:
+               return parse_test(string);
+       default:
+               SYNTAX_ERROR("Unexpected token \"%s\", expected test "
+                                       "operation, \"!\", or \"(\"\n",
+                                       TOK_TO_STR(token, string));
+               return NULL;
+       }
+}
+
+
+static struct expr *parse_expr(int subexp)
+{
+       struct expr *expr = get_atom();
+
+       while (expr) {
+               char *string;
+               int op = get_token(&string);
+
+               if (op == TOK_EOF) {
+                       if (subexp) {
+                               free_parse_tree(expr);
+                               SYNTAX_ERROR("Expected \"&&\", \"||\" or "
+                                               "\")\", got EOF\n");
+                               return NULL;
+                       }
+                       break;
+               }
+
+               if (op == TOK_CLOSE_BRACKET) {
+                       if (!subexp) {
+                               free_parse_tree(expr);
+                               SYNTAX_ERROR("Unexpected \")\", expected "
+                                               "\"&&\", \"!!\" or EOF\n");
+                               return NULL;
+                       }
+                       break;
+               }
+               
+               if (op != TOK_AND && op != TOK_OR) {
+                       free_parse_tree(expr);
+                       SYNTAX_ERROR("Unexpected token \"%s\", expected "
+                               "\"&&\" or \"||\"\n", TOK_TO_STR(op, string));
+                       return NULL;
+               }
+
+               expr = create_expr(expr, op, get_atom());
+       }
+
+       return expr;
+}
+
+
+/*
+ * Action parser
+ */
+int parse_action(char *s, int verbose)
+{
+       char *string, **argv = NULL;
+       int i, token, args = 0;
+       struct expr *expr;
+       struct action_entry *action;
+       void *data = NULL;
+       struct action **spec_list;
+       int spec_count;
+
+       cur_ptr = source = s;
+       token = get_token(&string);
+
+       if (token != TOK_STRING) {
+               SYNTAX_ERROR("Unexpected token \"%s\", expected name\n",
+                                               TOK_TO_STR(token, string));
+               return 0;
+       }
+
+       for (i = 0; action_table[i].args != -1; i++)
+               if (strcmp(string, action_table[i].name) == 0)
+                       break;
+
+       if (action_table[i].args == -1) {
+               SYNTAX_ERROR("Non-existent action \"%s\"\n", string);
+               return 0;
+       }
+
+       action = &action_table[i];
+
+       token = get_token(&string);
+
+       if (token == TOK_AT)
+               goto skip_args;
+
+       if (token != TOK_OPEN_BRACKET) {
+               SYNTAX_ERROR("Unexpected token \"%s\", expected \"(\"\n",
+                                               TOK_TO_STR(token, string));
+               goto failed;
+       }
+
+       /*
+        * speculatively read all the arguments, and then see if the
+        * number of arguments read is the number expected, this handles
+        * actions with a variable number of arguments
+        */
+       token = get_token(&string);
+       if (token == TOK_CLOSE_BRACKET)
+               goto skip_args;
+
+       while (1) {
+               if (token != TOK_STRING) {
+                       SYNTAX_ERROR("Unexpected token \"%s\", expected "
+                               "argument\n", TOK_TO_STR(token, string));
+                       goto failed;
+               }
+
+               argv = realloc(argv, (args + 1) * sizeof(char *));
+               if (argv == NULL)
+                       MEM_ERROR();
+
+               argv[args ++] = strdup(string);
+
+               token = get_token(&string);
+
+               if (token == TOK_CLOSE_BRACKET)
+                       break;
+
+               if (token != TOK_COMMA) {
+                       SYNTAX_ERROR("Unexpected token \"%s\", expected "
+                               "\",\" or \")\"\n", TOK_TO_STR(token, string));
+                       goto failed;
+               }
+               token = get_token(&string);
+       }
+
+skip_args:
+       /*
+        * expected number of arguments?
+        */
+       if(action->args != -2 && args != action->args) {
+               SYNTAX_ERROR("Unexpected number of arguments, expected %d, "
+                       "got %d\n", action->args, args);
+               goto failed;
+       }
+
+       if (action->parse_args) {
+               int res = action->parse_args(action, args, argv, &data);
+
+               if (res == 0)
+                       goto failed;
+       }
+
+       if (token == TOK_CLOSE_BRACKET)
+               token = get_token(&string);
+
+       if (token != TOK_AT) {
+               SYNTAX_ERROR("Unexpected token \"%s\", expected \"@\"\n",
+                                               TOK_TO_STR(token, string));
+               goto failed;
+       }
+       
+       parsing_action = action;
+       expr = parse_expr(0);
+
+       if (expr == NULL)
+               goto failed;
+
+       /*
+        * choose action list and increment action counter
+        */
+       switch(action->type) {
+       case FRAGMENT_ACTION:
+               spec_count = fragment_count ++;
+               spec_list = &fragment_spec;
+               break;
+       case EXCLUDE_ACTION:
+               spec_count = exclude_count ++;
+               spec_list = &exclude_spec;
+               break;
+       case EMPTY_ACTION:
+               spec_count = empty_count ++;
+               spec_list = &empty_spec;
+               break;
+       case MOVE_ACTION:
+               spec_count = move_count ++;
+               spec_list = &move_spec;
+               break;
+       case PRUNE_ACTION:
+               spec_count = prune_count ++;
+               spec_list = &prune_spec;
+               break;
+       default:
+               spec_count = other_count ++;
+               spec_list = &other_spec;
+       }
+       
+       *spec_list = realloc(*spec_list, (spec_count + 1) *
+                                       sizeof(struct action));
+       if (*spec_list == NULL)
+               MEM_ERROR();
+
+       (*spec_list)[spec_count].type = action->type;
+       (*spec_list)[spec_count].action = action;
+       (*spec_list)[spec_count].args = args;
+       (*spec_list)[spec_count].argv = argv;
+       (*spec_list)[spec_count].expr = expr;
+       (*spec_list)[spec_count].data = data;
+       (*spec_list)[spec_count].verbose = verbose;
+
+       return 1;
+
+failed:
+       free(argv);
+       return 0;
+}
+
+
+/*
+ * Evaluate expressions
+ */
+
+#define ALLOC_SZ 128
+
+#define LOG_ENABLE     0
+#define LOG_DISABLE    1
+#define LOG_PRINT      2
+#define LOG_ENABLED    3
+
+char *_expr_log(char *string, int cmnd)
+{
+       static char *expr_msg = NULL;
+       static int cur_size = 0, alloc_size = 0;
+       int size;
+
+       switch(cmnd) {
+       case LOG_ENABLE:
+               expr_msg = malloc(ALLOC_SZ);
+               alloc_size = ALLOC_SZ;
+               cur_size = 0;
+               return expr_msg;
+       case LOG_DISABLE:
+               free(expr_msg);
+               alloc_size = cur_size = 0;
+               return expr_msg = NULL;
+       case LOG_ENABLED:
+               return expr_msg;
+       default:
+               if(expr_msg == NULL)
+                       return NULL;
+               break;
+       }
+
+       /* if string is empty append '\0' */
+       size = strlen(string) ? : 1; 
+
+       if(alloc_size - cur_size < size) {
+               /* buffer too small, expand */
+               alloc_size = (cur_size + size + ALLOC_SZ - 1) & ~(ALLOC_SZ - 1);
+
+               expr_msg = realloc(expr_msg, alloc_size);
+               if(expr_msg == NULL)
+                       MEM_ERROR();
+       }
+
+       memcpy(expr_msg + cur_size, string, size);
+       cur_size += size; 
+
+       return expr_msg;
+}
+
+
+char *expr_log_cmnd(int cmnd)
+{
+       return _expr_log(NULL, cmnd);
+}
+
+
+char *expr_log(char *string)
+{
+       return _expr_log(string, LOG_PRINT);
+}
+
+
+void expr_log_atom(struct atom *atom)
+{
+       int i;
+
+       if(atom->test->handle_logging)
+               return;
+
+       expr_log(atom->test->name);
+
+       if(atom->args) {
+               expr_log("(");
+               for(i = 0; i < atom->args; i++) {
+                       expr_log(atom->argv[i]);
+                       if (i + 1 < atom->args)
+                               expr_log(",");
+               }
+               expr_log(")");
+       }
+}
+
+
+void expr_log_match(int match)
+{
+       if(match)
+               expr_log("=True");
+       else
+               expr_log("=False");
+}
+
+
+static int eval_expr_log(struct expr *expr, struct action_data *action_data)
+{
+       int match;
+
+       switch (expr->type) {
+       case ATOM_TYPE:
+               expr_log_atom(&expr->atom);
+               match = expr->atom.test->fn(&expr->atom, action_data);
+               expr_log_match(match);
+               break;
+       case UNARY_TYPE:
+               expr_log("!");
+               match = !eval_expr_log(expr->unary_op.expr, action_data);
+               break;
+       default:
+               expr_log("(");
+               match = eval_expr_log(expr->expr_op.lhs, action_data);
+
+               if ((expr->expr_op.op == TOK_AND && match) ||
+                               (expr->expr_op.op == TOK_OR && !match)) {
+                       expr_log(token_table[expr->expr_op.op].string);
+                       match = eval_expr_log(expr->expr_op.rhs, action_data);
+               }
+               expr_log(")");
+               break;
+       }
+
+       return match;
+}
+
+
+static int eval_expr(struct expr *expr, struct action_data *action_data)
+{
+       int match;
+
+       switch (expr->type) {
+       case ATOM_TYPE:
+               match = expr->atom.test->fn(&expr->atom, action_data);
+               break;
+       case UNARY_TYPE:
+               match = !eval_expr(expr->unary_op.expr, action_data);
+               break;
+       default:
+               match = eval_expr(expr->expr_op.lhs, action_data);
+
+               if ((expr->expr_op.op == TOK_AND && match) ||
+                                       (expr->expr_op.op == TOK_OR && !match))
+                       match = eval_expr(expr->expr_op.rhs, action_data);
+               break;
+       }
+
+       return match;
+}
+
+
+static int eval_expr_top(struct action *action, struct action_data *action_data)
+{
+       if(action->verbose) {
+               int match, n;
+
+               expr_log_cmnd(LOG_ENABLE);
+
+               if(action_data->subpath)
+                       expr_log(action_data->subpath);
+
+               expr_log("=");
+               expr_log(action->action->name);
+
+               if(action->args) {
+                       expr_log("(");
+                       for (n = 0; n < action->args; n++) {
+                               expr_log(action->argv[n]);
+                               if(n + 1 < action->args)
+                                       expr_log(",");
+                       }
+                       expr_log(")");
+               }
+
+               expr_log("@");
+
+               match = eval_expr_log(action->expr, action_data);
+
+               /*
+                * Print the evaluated expression log, if the
+                * result matches the logging specified
+                */
+               if((match && (action->verbose & ACTION_LOG_TRUE)) || (!match
+                               && (action->verbose & ACTION_LOG_FALSE)))
+                       progressbar_info("%s\n", expr_log(""));
+
+               expr_log_cmnd(LOG_DISABLE);
+
+               return match;
+       } else
+               return eval_expr(action->expr, action_data);
+}
+
+
+/*
+ * Read action file, passing each line to parse_action() for
+ * parsing.
+ *
+ * One action per line, of the form
+ *     action(arg1,arg2)@expr(arg1,arg2)....
+ *
+ * Actions can be split across multiple lines using "\".
+ * 
+ * Blank lines and comment lines indicated by # are supported.
+ */
+int parse_action_true(char *s)
+{
+       return parse_action(s, ACTION_LOG_TRUE);
+}
+
+
+int parse_action_false(char *s)
+{
+       return parse_action(s, ACTION_LOG_FALSE);
+}
+
+
+int parse_action_verbose(char *s)
+{
+       return parse_action(s, ACTION_LOG_VERBOSE);
+}
+
+
+int parse_action_nonverbose(char *s)
+{
+       return parse_action(s, ACTION_LOG_NONE);
+}
+
+
+int read_action_file(char *filename, int verbose)
+{
+       switch(verbose) {
+       case ACTION_LOG_TRUE:
+               return read_file(filename, "action", parse_action_true);
+       case ACTION_LOG_FALSE:
+               return read_file(filename, "action", parse_action_false);
+       case ACTION_LOG_VERBOSE:
+               return read_file(filename, "action", parse_action_verbose);
+       default:
+               return read_file(filename, "action", parse_action_nonverbose);
+       }
+}
+
+
+/*
+ * helper to evaluate whether action/test acts on this file type
+ */
+static int file_type_match(int st_mode, int type)
+{
+       switch(type) {
+       case ACTION_DIR:
+               return S_ISDIR(st_mode);
+       case ACTION_REG:
+               return S_ISREG(st_mode);
+       case ACTION_ALL:
+               return S_ISREG(st_mode) || S_ISDIR(st_mode) ||
+                       S_ISCHR(st_mode) || S_ISBLK(st_mode) ||
+                       S_ISFIFO(st_mode) || S_ISSOCK(st_mode);
+       case ACTION_LNK:
+               return S_ISLNK(st_mode);
+       case ACTION_ALL_LNK:
+       default:
+               return 1;
+       }
+}
+
+
+/*
+ * General action evaluation code
+ */
+int actions()
+{
+       return other_count;
+}
+
+
+void eval_actions(struct dir_info *root, struct dir_ent *dir_ent)
+{
+       int i, match;
+       struct action_data action_data;
+       int st_mode = dir_ent->inode->buf.st_mode;
+
+       action_data.name = dir_ent->name;
+       action_data.pathname = strdup(pathname(dir_ent));
+       action_data.subpath = strdup(subpathname(dir_ent));
+       action_data.buf = &dir_ent->inode->buf;
+       action_data.depth = dir_ent->our_dir->depth;
+       action_data.dir_ent = dir_ent;
+       action_data.root = root;
+
+       for (i = 0; i < other_count; i++) {
+               struct action *action = &other_spec[i];
+
+               if (!file_type_match(st_mode, action->action->file_types))
+                       /* action does not operate on this file type */
+                       continue;
+
+               match = eval_expr_top(action, &action_data);
+
+               if (match)
+                       action->action->run_action(action, dir_ent);
+       }
+
+       free(action_data.pathname);
+       free(action_data.subpath);
+}
+
+
+/*
+ * Fragment specific action code
+ */
+void *eval_frag_actions(struct dir_info *root, struct dir_ent *dir_ent)
+{
+       int i, match;
+       struct action_data action_data;
+
+       action_data.name = dir_ent->name;
+       action_data.pathname = strdup(pathname(dir_ent));
+       action_data.subpath = strdup(subpathname(dir_ent));
+       action_data.buf = &dir_ent->inode->buf;
+       action_data.depth = dir_ent->our_dir->depth;
+       action_data.dir_ent = dir_ent;
+       action_data.root = root;
+
+       for (i = 0; i < fragment_count; i++) {
+               match = eval_expr_top(&fragment_spec[i], &action_data);
+               if (match) {
+                       free(action_data.pathname);
+                       free(action_data.subpath);
+                       return &fragment_spec[i].data;
+               }
+       }
+
+       free(action_data.pathname);
+       free(action_data.subpath);
+       return &def_fragment;
+}
+
+
+void *get_frag_action(void *fragment)
+{
+       struct action *spec_list_end = &fragment_spec[fragment_count];
+       struct action *action;
+
+       if (fragment == NULL)
+               return &def_fragment;
+
+       if (fragment_count == 0)
+               return NULL;
+
+       if (fragment == &def_fragment)
+               action = &fragment_spec[0] - 1;
+       else 
+               action = fragment - offsetof(struct action, data);
+
+       if (++action == spec_list_end)
+               return NULL;
+
+       return &action->data;
+}
+
+
+/*
+ * Exclude specific action code
+ */
+int exclude_actions()
+{
+       return exclude_count;
+}
+
+
+int eval_exclude_actions(char *name, char *pathname, char *subpath,
+       struct stat *buf, int depth, struct dir_ent *dir_ent)
+{
+       int i, match = 0;
+       struct action_data action_data;
+
+       action_data.name = name;
+       action_data.pathname = pathname;
+       action_data.subpath = subpath;
+       action_data.buf = buf;
+       action_data.depth = depth;
+       action_data.dir_ent = dir_ent;
+
+       for (i = 0; i < exclude_count && !match; i++)
+               match = eval_expr_top(&exclude_spec[i], &action_data);
+
+       return match;
+}
+
+
+/*
+ * Fragment specific action code
+ */
+static void frag_action(struct action *action, struct dir_ent *dir_ent)
+{
+       struct inode_info *inode = dir_ent->inode;
+
+       inode->no_fragments = 0;
+}
+
+static void no_frag_action(struct action *action, struct dir_ent *dir_ent)
+{
+       struct inode_info *inode = dir_ent->inode;
+
+       inode->no_fragments = 1;
+}
+
+static void always_frag_action(struct action *action, struct dir_ent *dir_ent)
+{
+       struct inode_info *inode = dir_ent->inode;
+
+       inode->always_use_fragments = 1;
+}
+
+static void no_always_frag_action(struct action *action, struct dir_ent *dir_ent)
+{
+       struct inode_info *inode = dir_ent->inode;
+
+       inode->always_use_fragments = 0;
+}
+
+
+/*
+ * Compression specific action code
+ */
+static void comp_action(struct action *action, struct dir_ent *dir_ent)
+{
+       struct inode_info *inode = dir_ent->inode;
+
+       inode->noD = inode->noF = 0;
+}
+
+static void uncomp_action(struct action *action, struct dir_ent *dir_ent)
+{
+       struct inode_info *inode = dir_ent->inode;
+
+       inode->noD = inode->noF = 1;
+}
+
+
+/*
+ * Uid/gid specific action code
+ */
+static long long parse_uid(char *arg) {
+       char *b;
+       long long uid = strtoll(arg, &b, 10);
+
+       if (*b == '\0') {
+               if (uid < 0 || uid >= (1LL << 32)) {
+                       SYNTAX_ERROR("Uid out of range\n");
+                       return -1;
+               }
+       } else {
+               struct passwd *passwd = getpwnam(arg);
+
+               if (passwd)
+                       uid = passwd->pw_uid;
+               else {
+                       SYNTAX_ERROR("Invalid uid or unknown user\n");
+                       return -1;
+               }
+       }
+
+       return uid;
+}
+
+
+static long long parse_gid(char *arg) {
+       char *b;
+       long long gid = strtoll(arg, &b, 10);
+
+       if (*b == '\0') {
+               if (gid < 0 || gid >= (1LL << 32)) {
+                       SYNTAX_ERROR("Gid out of range\n");
+                       return -1;
+               }
+       } else {
+               struct group *group = getgrnam(arg);
+
+               if (group)
+                       gid = group->gr_gid;
+               else {
+                       SYNTAX_ERROR("Invalid gid or unknown group\n");
+                       return -1;
+               }
+       }
+
+       return gid;
+}
+
+
+static int parse_uid_args(struct action_entry *action, int args, char **argv,
+                                                               void **data)
+{
+       long long uid;
+       struct uid_info *uid_info;
+
+       uid = parse_uid(argv[0]);
+       if (uid == -1)
+               return 0;
+
+       uid_info = malloc(sizeof(struct uid_info));
+       if (uid_info == NULL)
+               MEM_ERROR();
+
+       uid_info->uid = uid;
+       *data = uid_info;
+
+       return 1;
+}
+
+
+static int parse_gid_args(struct action_entry *action, int args, char **argv,
+                                                               void **data)
+{
+       long long gid;
+       struct gid_info *gid_info;
+
+       gid = parse_gid(argv[0]);
+       if (gid == -1)
+               return 0;
+
+       gid_info = malloc(sizeof(struct gid_info));
+       if (gid_info == NULL)
+               MEM_ERROR();
+
+       gid_info->gid = gid;
+       *data = gid_info;
+
+       return 1;
+}
+
+
+static int parse_guid_args(struct action_entry *action, int args, char **argv,
+                                                               void **data)
+{
+       long long uid, gid;
+       struct guid_info *guid_info;
+
+       uid = parse_uid(argv[0]);
+       if (uid == -1)
+               return 0;
+
+       gid = parse_gid(argv[1]);
+       if (gid == -1)
+               return 0;
+
+       guid_info = malloc(sizeof(struct guid_info));
+       if (guid_info == NULL)
+               MEM_ERROR();
+
+       guid_info->uid = uid;
+       guid_info->gid = gid;
+       *data = guid_info;
+
+       return 1;
+}
+
+
+static void uid_action(struct action *action, struct dir_ent *dir_ent)
+{
+       struct inode_info *inode = dir_ent->inode;
+       struct uid_info *uid_info = action->data;
+
+       inode->buf.st_uid = uid_info->uid;
+}
+
+static void gid_action(struct action *action, struct dir_ent *dir_ent)
+{
+       struct inode_info *inode = dir_ent->inode;
+       struct gid_info *gid_info = action->data;
+
+       inode->buf.st_gid = gid_info->gid;
+}
+
+static void guid_action(struct action *action, struct dir_ent *dir_ent)
+{
+       struct inode_info *inode = dir_ent->inode;
+       struct guid_info *guid_info = action->data;
+
+       inode->buf.st_uid = guid_info->uid;
+       inode->buf.st_gid = guid_info->gid;
+
+}
+
+
+/*
+ * Mode specific action code
+ */
+static int parse_octal_mode_args(int args, char **argv,
+                       void **data)
+{
+       int n, bytes;
+       unsigned int mode;
+       struct mode_data *mode_data;
+
+       /* octal mode number? */
+       n = sscanf(argv[0], "%o%n", &mode, &bytes);
+       if (n == 0)
+               return -1; /* not an octal number arg */
+
+
+       /* check there's no trailing junk */
+       if (argv[0][bytes] != '\0') {
+               SYNTAX_ERROR("Unexpected trailing bytes after octal "
+                       "mode number\n");
+               return 0; /* bad octal number arg */
+       }
+
+       /* check there's only one argument */
+       if (args > 1) {
+               SYNTAX_ERROR("Octal mode number is first argument, "
+                       "expected one argument, got %d\n", args);
+               return 0; /* bad octal number arg */
+       }
+
+       /*  check mode is within range */
+       if (mode > 07777) {
+               SYNTAX_ERROR("Octal mode %o is out of range\n", mode);
+               return 0; /* bad octal number arg */
+       }
+
+       mode_data = malloc(sizeof(struct mode_data));
+       if (mode_data == NULL)
+               MEM_ERROR();
+
+       mode_data->operation = ACTION_MODE_OCT;
+       mode_data->mode = mode;
+       mode_data->next = NULL;
+       *data = mode_data;
+
+       return 1;
+}
+
+
+/*
+ * Parse symbolic mode of format [ugoa]*[[+-=]PERMS]+
+ * PERMS = [rwxXst]+ or [ugo]
+ */
+static int parse_sym_mode_arg(char *arg, struct mode_data **head,
+       struct mode_data **cur)
+{
+       struct mode_data *mode_data;
+       int mode;
+       int mask = 0;
+       int op;
+       char X;
+
+       if (arg[0] != 'u' && arg[0] != 'g' && arg[0] != 'o' && arg[0] != 'a') {
+               /* no ownership specifiers, default to a */
+               mask = 0777;
+               goto parse_operation;
+       }
+
+       /* parse ownership specifiers */
+       while(1) {
+               switch(*arg) {
+               case 'u':
+                       mask |= 04700;
+                       break;
+               case 'g':
+                       mask |= 02070;
+                       break;
+               case 'o':
+                       mask |= 01007;
+                       break;
+               case 'a':
+                       mask = 07777;
+                       break;
+               default:
+                       goto parse_operation;
+               }
+               arg ++;
+       }
+
+parse_operation:
+       /* trap a symbolic mode with just an ownership specification */
+       if(*arg == '\0') {
+               SYNTAX_ERROR("Expected one of '+', '-' or '=', got EOF\n");
+               goto failed;
+       }
+
+       while(*arg != '\0') {
+               mode = 0;
+               X = 0;
+
+               switch(*arg) {
+               case '+':
+                       op = ACTION_MODE_ADD;
+                       break;
+               case '-':
+                       op = ACTION_MODE_REM;
+                       break;
+               case '=':
+                       op = ACTION_MODE_SET;
+                       break;
+               default:
+                       SYNTAX_ERROR("Expected one of '+', '-' or '=', got "
+                               "'%c'\n", *arg);
+                       goto failed;
+               }
+       
+               arg ++;
+       
+               /* Parse PERMS */
+               if (*arg == 'u' || *arg == 'g' || *arg == 'o') {
+                       /* PERMS = [ugo] */
+                       mode = - *arg;
+                       arg ++;
+               } else {
+                       /* PERMS = [rwxXst]* */
+                       while(1) {
+                               switch(*arg) {
+                               case 'r':
+                                       mode |= 0444;
+                                       break;
+                               case 'w':
+                                       mode |= 0222;
+                                       break;
+                               case 'x':
+                                       mode |= 0111;
+                                       break;
+                               case 's':
+                                       mode |= 06000;
+                                       break;
+                               case 't':
+                                       mode |= 01000;
+                                       break;
+                               case 'X':
+                                       X = 1;
+                                       break;
+                               case '+':
+                               case '-':
+                               case '=':
+                               case '\0':
+                                       mode &= mask;
+                                       goto perms_parsed;
+                               default:
+                                       SYNTAX_ERROR("Unrecognised permission "
+                                                               "'%c'\n", *arg);
+                                       goto failed;
+                               }
+       
+                               arg ++;
+                       }
+               }
+       
+perms_parsed:
+               mode_data = malloc(sizeof(*mode_data));
+               if (mode_data == NULL)
+                       MEM_ERROR();
+
+               mode_data->operation = op;
+               mode_data->mode = mode;
+               mode_data->mask = mask;
+               mode_data->X = X;
+               mode_data->next = NULL;
+
+               if (*cur) {
+                       (*cur)->next = mode_data;
+                       *cur = mode_data;
+               } else
+                       *head = *cur = mode_data;
+       }
+
+       return 1;
+
+failed:
+       return 0;
+}
+
+
+static int parse_sym_mode_args(struct action_entry *action, int args,
+                                       char **argv, void **data)
+{
+       int i, res = 1;
+       struct mode_data *head = NULL, *cur = NULL;
+
+       for (i = 0; i < args && res; i++)
+               res = parse_sym_mode_arg(argv[i], &head, &cur);
+
+       *data = head;
+
+       return res;
+}
+
+
+static int parse_mode_args(struct action_entry *action, int args,
+                                       char **argv, void **data)
+{
+       int res;
+
+       if (args == 0) {
+               SYNTAX_ERROR("Mode action expects one or more arguments\n");
+               return 0;
+       }
+
+       res = parse_octal_mode_args(args, argv, data);
+       if(res >= 0)
+               /* Got an octal mode argument */
+               return res;
+       else  /* not an octal mode argument */
+               return parse_sym_mode_args(action, args, argv, data);
+}
+
+
+static int mode_execute(struct mode_data *mode_data, int st_mode)
+{
+       int mode = 0;
+
+       for (;mode_data; mode_data = mode_data->next) {
+               if (mode_data->mode < 0) {
+                       /* 'u', 'g' or 'o' */
+                       switch(-mode_data->mode) {
+                       case 'u':
+                               mode = (st_mode >> 6) & 07;
+                               break;
+                       case 'g':
+                               mode = (st_mode >> 3) & 07;
+                               break;
+                       case 'o':
+                               mode = st_mode & 07;
+                               break;
+                       }
+                       mode = ((mode << 6) | (mode << 3) | mode) &
+                               mode_data->mask;
+               } else if (mode_data->X &&
+                               ((st_mode & S_IFMT) == S_IFDIR ||
+                               (st_mode & 0111)))
+                       /* X permission, only takes effect if inode is a
+                        * directory or x is set for some owner */
+                       mode = mode_data->mode | (0111 & mode_data->mask);
+               else
+                       mode = mode_data->mode;
+
+               switch(mode_data->operation) {
+               case ACTION_MODE_OCT:
+                       st_mode = (st_mode & S_IFMT) | mode;
+                       break;
+               case ACTION_MODE_SET:
+                       st_mode = (st_mode & ~mode_data->mask) | mode;
+                       break;
+               case ACTION_MODE_ADD:
+                       st_mode |= mode;
+                       break;
+               case ACTION_MODE_REM:
+                       st_mode &= ~mode;
+               }
+       }
+
+       return st_mode;
+}
+
+
+static void mode_action(struct action *action, struct dir_ent *dir_ent)
+{
+       dir_ent->inode->buf.st_mode = mode_execute(action->data,
+                                       dir_ent->inode->buf.st_mode);
+}
+
+
+/*
+ *  Empty specific action code
+ */
+int empty_actions()
+{
+       return empty_count;
+}
+
+
+static int parse_empty_args(struct action_entry *action, int args,
+                                       char **argv, void **data)
+{
+       struct empty_data *empty_data;
+       int val;
+
+       if (args >= 2) {
+               SYNTAX_ERROR("Empty action expects zero or one argument\n");
+               return 0;
+       }
+
+       if (args == 0 || strcmp(argv[0], "all") == 0)
+               val = EMPTY_ALL;
+       else if (strcmp(argv[0], "source") == 0)
+               val = EMPTY_SOURCE;
+       else if (strcmp(argv[0], "excluded") == 0)
+               val = EMPTY_EXCLUDED;
+       else {
+               SYNTAX_ERROR("Empty action expects zero arguments, or one"
+                       "argument containing \"all\", \"source\", or \"excluded\""
+                       "\n");
+               return 0;
+       }
+
+       empty_data = malloc(sizeof(*empty_data));
+       if (empty_data == NULL)
+               MEM_ERROR();
+
+       empty_data->val = val;
+       *data = empty_data;
+
+       return 1;
+}
+
+
+int eval_empty_actions(struct dir_info *root, struct dir_ent *dir_ent)
+{
+       int i, match = 0;
+       struct action_data action_data;
+       struct empty_data *data;
+       struct dir_info *dir = dir_ent->dir;
+
+       /*
+        * Empty action only works on empty directories
+        */
+       if (dir->count != 0)
+               return 0;
+
+       action_data.name = dir_ent->name;
+       action_data.pathname = strdup(pathname(dir_ent));
+       action_data.subpath = strdup(subpathname(dir_ent));
+       action_data.buf = &dir_ent->inode->buf;
+       action_data.depth = dir_ent->our_dir->depth;
+       action_data.dir_ent = dir_ent;
+       action_data.root = root;
+
+       for (i = 0; i < empty_count && !match; i++) {
+               data = empty_spec[i].data;
+
+               /*
+                * determine the cause of the empty directory and evaluate
+                * the empty action specified.  Three empty actions:
+                * - EMPTY_SOURCE: empty action triggers only if the directory
+                *      was originally empty, i.e directories that are empty
+                *      only due to excluding are ignored.
+                * - EMPTY_EXCLUDED: empty action triggers only if the directory
+                *      is empty because of excluding, i.e. directories that
+                *      were originally empty are ignored.
+                * - EMPTY_ALL (the default): empty action triggers if the
+                *      directory is empty, irrespective of the reason, i.e.
+                *      the directory could have been originally empty or could
+                *      be empty due to excluding.
+                */
+               if ((data->val == EMPTY_EXCLUDED && !dir->excluded) ||
+                               (data->val == EMPTY_SOURCE && dir->excluded))
+                       continue;
+               
+               match = eval_expr_top(&empty_spec[i], &action_data);
+       }
+
+       free(action_data.pathname);
+       free(action_data.subpath);
+
+       return match;
+}
+
+
+/*
+ *  Move specific action code
+ */
+static struct move_ent *move_list = NULL;
+
+
+int move_actions()
+{
+       return move_count;
+}
+
+
+static char *move_pathname(struct move_ent *move)
+{
+       struct dir_info *dest;
+       char *name, *pathname;
+       int res;
+
+       dest = (move->ops & ACTION_MOVE_MOVE) ?
+               move->dest : move->dir_ent->our_dir;
+       name = (move->ops & ACTION_MOVE_RENAME) ?
+               move->name : move->dir_ent->name;
+
+       if(dest->subpath[0] != '\0')
+               res = asprintf(&pathname, "%s/%s", dest->subpath, name);
+       else
+               res = asprintf(&pathname, "/%s", name);
+
+       if(res == -1)
+               BAD_ERROR("asprintf failed in move_pathname\n");
+
+       return pathname;
+}
+
+
+static char *get_comp(char **pathname)
+{
+       char *path = *pathname, *start;
+
+       while(*path == '/')
+               path ++;
+
+       if(*path == '\0')
+               return NULL;
+
+       start = path;
+       while(*path != '/' && *path != '\0')
+               path ++;
+
+       *pathname = path;
+       return strndup(start, path - start);
+}
+
+
+static struct dir_ent *lookup_comp(char *comp, struct dir_info *dest)
+{
+       struct dir_ent *dir_ent;
+
+       for(dir_ent = dest->list; dir_ent; dir_ent = dir_ent->next)
+               if(strcmp(comp, dir_ent->name) == 0)
+                       break;
+
+       return dir_ent;
+}
+
+
+void eval_move(struct action_data *action_data, struct move_ent *move,
+               struct dir_info *root, struct dir_ent *dir_ent, char *pathname)
+{
+       struct dir_info *dest, *source = dir_ent->our_dir;
+       struct dir_ent *comp_ent;
+       char *comp, *path = pathname;
+
+       /*
+        * Walk pathname to get the destination directory
+        *
+        * Like the mv command, if the last component exists and it
+        * is a directory, then move the file into that directory,
+        * otherwise, move the file into parent directory of the last
+        * component and rename to the last component.
+        */
+       if (pathname[0] == '/')
+               /* absolute pathname, walk from root directory */
+               dest = root;
+       else
+               /* relative pathname, walk from current directory */
+               dest = source;
+
+       for(comp = get_comp(&pathname); comp; free(comp),
+                                               comp = get_comp(&pathname)) {
+
+               if (strcmp(comp, ".") == 0)
+                       continue;
+
+               if (strcmp(comp, "..") == 0) {
+                       /* if we're in the root directory then ignore */
+                       if(dest->depth > 1)
+                               dest = dest->dir_ent->our_dir;
+                       continue;
+               }
+
+               /*
+                * Look up comp in current directory, if it exists and it is a
+                * directory continue walking the pathname, otherwise exit,
+                * we've walked as far as we can go, normally this is because
+                * we've arrived at the leaf component which we are going to
+                * rename source to
+                */
+               comp_ent = lookup_comp(comp, dest);
+               if (comp_ent == NULL || (comp_ent->inode->buf.st_mode & S_IFMT)
+                                                       != S_IFDIR)
+                       break;
+
+               dest = comp_ent->dir;
+       }
+
+       if(comp) {
+               /* Leaf component? If so we're renaming to this  */
+               char *remainder = get_comp(&pathname);
+               free(remainder);
+
+               if(remainder) {
+                       /*
+                        * trying to move source to a subdirectory of
+                        * comp, but comp either doesn't exist, or it isn't
+                        * a directory, which is impossible
+                        */
+                       if (comp_ent == NULL)
+                               ERROR("Move action: cannot move %s to %s, no "
+                                       "such directory %s\n",
+                                       action_data->subpath, path, comp);
+                       else
+                               ERROR("Move action: cannot move %s to %s, %s "
+                                       "is not a directory\n",
+                                       action_data->subpath, path, comp);
+                       free(comp);
+                       return;
+               }
+
+               /*
+                * Multiple move actions triggering on one file can be merged
+                * if one is a RENAME and the other is a MOVE.  Multiple RENAMEs
+                * can only merge if they're doing the same thing
+                */
+               if(move->ops & ACTION_MOVE_RENAME) {
+                       if(strcmp(comp, move->name) != 0) {
+                               char *conf_path = move_pathname(move);
+                               ERROR("Move action: Cannot move %s to %s, "
+                                       "conflicting move, already moving "
+                                       "to %s via another move action!\n",
+                                       action_data->subpath, path, conf_path);
+                               free(conf_path);
+                               free(comp);
+                               return;
+                       }
+                       free(comp);
+               } else {
+                       move->name = comp;
+                       move->ops |= ACTION_MOVE_RENAME;
+               }
+       }
+
+       if(dest != source) {
+               /*
+                * Multiple move actions triggering on one file can be merged
+                * if one is a RENAME and the other is a MOVE.  Multiple MOVEs
+                * can only merge if they're doing the same thing
+                */
+               if(move->ops & ACTION_MOVE_MOVE) {
+                       if(dest != move->dest) {
+                               char *conf_path = move_pathname(move);
+                               ERROR("Move action: Cannot move %s to %s, "
+                                       "conflicting move, already moving "
+                                       "to %s via another move action!\n",
+                                       action_data->subpath, path, conf_path);
+                               free(conf_path);
+                               return;
+                       }
+               } else {
+                       move->dest = dest;
+                       move->ops |= ACTION_MOVE_MOVE;
+               }
+       }
+}
+
+
+static int subdirectory(struct dir_info *source, struct dir_info *dest)
+{
+       if(source == NULL)
+               return 0;
+
+       return strlen(source->subpath) <= strlen(dest->subpath) &&
+               (dest->subpath[strlen(source->subpath)] == '/' ||
+               dest->subpath[strlen(source->subpath)] == '\0') &&
+               strncmp(source->subpath, dest->subpath,
+               strlen(source->subpath)) == 0;
+}
+
+
+void eval_move_actions(struct dir_info *root, struct dir_ent *dir_ent)
+{
+       int i;
+       struct action_data action_data;
+       struct move_ent *move = NULL;
+
+       action_data.name = dir_ent->name;
+       action_data.pathname = strdup(pathname(dir_ent));
+       action_data.subpath = strdup(subpathname(dir_ent));
+       action_data.buf = &dir_ent->inode->buf;
+       action_data.depth = dir_ent->our_dir->depth;
+       action_data.dir_ent = dir_ent;
+       action_data.root = root;
+
+       /*
+        * Evaluate each move action against the current file.  For any
+        * move actions that match don't actually perform the move now, but,
+        * store it, and execute all the stored move actions together once the
+        * directory scan is complete.  This is done to ensure each separate
+        * move action does not nondeterministically interfere with other move
+        * actions.  Each move action is considered to act independently, and
+        * each move action sees the directory tree in the same state.
+        */
+       for (i = 0; i < move_count; i++) {
+               struct action *action = &move_spec[i];
+               int match = eval_expr_top(action, &action_data);
+
+               if(match) {
+                       if(move == NULL) {
+                               move = malloc(sizeof(*move));
+                               if(move == NULL)
+                                       MEM_ERROR();
+
+                               move->ops = 0;
+                               move->dir_ent = dir_ent;
+                       }
+                       eval_move(&action_data, move, root, dir_ent,
+                               action->argv[0]);
+               }
+       }
+
+       if(move) {
+               struct dir_ent *comp_ent;
+               struct dir_info *dest;
+               char *name;
+
+               /*
+                * Move contains the result of all triggered move actions.
+                * Check the destination doesn't already exist
+                */
+               if(move->ops == 0) {
+                       free(move);
+                       goto finish;
+               }
+
+               dest = (move->ops & ACTION_MOVE_MOVE) ?
+                       move->dest : dir_ent->our_dir;
+               name = (move->ops & ACTION_MOVE_RENAME) ?
+                       move->name : dir_ent->name;
+               comp_ent = lookup_comp(name, dest);
+               if(comp_ent) {
+                       char *conf_path = move_pathname(move);
+                       ERROR("Move action: Cannot move %s to %s, "
+                               "destination already exists\n",
+                               action_data.subpath, conf_path);
+                       free(conf_path);
+                       free(move);
+                       goto finish;
+               }
+
+               /*
+                * If we're moving a directory, check we're not moving it to a
+                * subdirectory of itself
+                */
+               if(subdirectory(dir_ent->dir, dest)) {
+                       char *conf_path = move_pathname(move);
+                       ERROR("Move action: Cannot move %s to %s, this is a "
+                               "subdirectory of itself\n",
+                               action_data.subpath, conf_path);
+                       free(conf_path);
+                       free(move);
+                       goto finish;
+               }
+               move->next = move_list;
+               move_list = move;
+       }
+
+finish:
+       free(action_data.pathname);
+       free(action_data.subpath);
+}
+
+
+static void move_dir(struct dir_ent *dir_ent)
+{
+       struct dir_info *dir = dir_ent->dir;
+       struct dir_ent *comp_ent;
+
+       /* update our directory's subpath name */
+       free(dir->subpath);
+       dir->subpath = strdup(subpathname(dir_ent));
+
+       /* recursively update the subpaths of any sub-directories */
+       for(comp_ent = dir->list; comp_ent; comp_ent = comp_ent->next)
+               if(comp_ent->dir)
+                       move_dir(comp_ent);
+}
+
+
+static void move_file(struct move_ent *move_ent)
+{
+       struct dir_ent *dir_ent = move_ent->dir_ent;
+
+       if(move_ent->ops & ACTION_MOVE_MOVE) {
+               struct dir_ent *comp_ent, *prev = NULL;
+               struct dir_info *source = dir_ent->our_dir,
+                                                       *dest = move_ent->dest;
+               char *filename = pathname(dir_ent);
+
+               /*
+                * If we're moving a directory, check we're not moving it to a
+                * subdirectory of itself
+                */
+               if(subdirectory(dir_ent->dir, dest)) {
+                       char *conf_path = move_pathname(move_ent);
+                       ERROR("Move action: Cannot move %s to %s, this is a "
+                               "subdirectory of itself\n",
+                               subpathname(dir_ent), conf_path);
+                       free(conf_path);
+                       return;
+               }
+
+               /* Remove the file from source directory */
+               for(comp_ent = source->list; comp_ent != dir_ent;
+                               prev = comp_ent, comp_ent = comp_ent->next);
+
+               if(prev)
+                       prev->next = comp_ent->next;
+               else
+                       source->list = comp_ent->next;
+
+               source->count --;
+               if((comp_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR)
+                       source->directory_count --;
+
+               /* Add the file to dest directory */
+               comp_ent->next = dest->list;
+               dest->list = comp_ent;
+               comp_ent->our_dir = dest;
+
+               dest->count ++;
+               if((comp_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR)
+                       dest->directory_count ++;
+
+               /*
+                * We've moved the file, and so we can't now use the
+                * parent directory's pathname to calculate the pathname
+                */
+               if(dir_ent->nonstandard_pathname == NULL) {
+                       dir_ent->nonstandard_pathname = strdup(filename);
+                       if(dir_ent->source_name) {
+                               free(dir_ent->source_name);
+                               dir_ent->source_name = NULL;
+                       }
+               }
+       }
+
+       if(move_ent->ops & ACTION_MOVE_RENAME) {
+               /*
+                * If we're using name in conjunction with the parent
+                * directory's pathname to calculate the pathname, we need
+                * to use source_name to override.  Otherwise it's already being
+                * over-ridden
+                */
+               if(dir_ent->nonstandard_pathname == NULL &&
+                                               dir_ent->source_name == NULL)
+                       dir_ent->source_name = dir_ent->name;
+               else
+                       free(dir_ent->name);
+
+               dir_ent->name = move_ent->name;
+       }
+
+       if(dir_ent->dir)
+               /*
+                * dir_ent is a directory, and we have to recursively fix-up
+                * its subpath, and the subpaths of all of its sub-directories
+                */
+               move_dir(dir_ent);
+}
+
+
+void do_move_actions()
+{
+       while(move_list) {
+               struct move_ent *temp = move_list;
+               struct dir_info *dest = (move_list->ops & ACTION_MOVE_MOVE) ?
+                       move_list->dest : move_list->dir_ent->our_dir;
+               char *name = (move_list->ops & ACTION_MOVE_RENAME) ?
+                       move_list->name : move_list->dir_ent->name;
+               struct dir_ent *comp_ent = lookup_comp(name, dest);
+               if(comp_ent) {
+                       char *conf_path = move_pathname(move_list);
+                       ERROR("Move action: Cannot move %s to %s, "
+                               "destination already exists\n",
+                               subpathname(move_list->dir_ent), conf_path);
+                       free(conf_path);
+               } else
+                       move_file(move_list);
+
+               move_list = move_list->next;
+               free(temp);
+       }
+}
+
+
+/*
+ * Prune specific action code
+ */
+int prune_actions()
+{
+       return prune_count;
+}
+
+
+int eval_prune_actions(struct dir_info *root, struct dir_ent *dir_ent)
+{
+       int i, match = 0;
+       struct action_data action_data;
+
+       action_data.name = dir_ent->name;
+       action_data.pathname = strdup(pathname(dir_ent));
+       action_data.subpath = strdup(subpathname(dir_ent));
+       action_data.buf = &dir_ent->inode->buf;
+       action_data.depth = dir_ent->our_dir->depth;
+       action_data.dir_ent = dir_ent;
+       action_data.root = root;
+
+       for (i = 0; i < prune_count && !match; i++)
+               match = eval_expr_top(&prune_spec[i], &action_data);
+
+       free(action_data.pathname);
+       free(action_data.subpath);
+
+       return match;
+}
+
+
+/*
+ * Noop specific action code
+ */
+static void noop_action(struct action *action, struct dir_ent *dir_ent)
+{
+}
+
+
+/*
+ * General test evaluation code
+ */
+
+/*
+ * A number can be of the form [range]number[size]
+ * [range] is either:
+ *     '<' or '-', match on less than number
+ *     '>' or '+', match on greater than number
+ *     '' (nothing), match on exactly number
+ * [size] is either:
+ *     '' (nothing), number
+ *     'k' or 'K', number * 2^10
+ *     'm' or 'M', number * 2^20
+ *     'g' or 'G', number * 2^30
+ */
+static int parse_number(char *start, long long *size, int *range, char **error)
+{
+       char *end;
+       long long number;
+
+       if (*start == '>' || *start == '+') {
+               *range = NUM_GREATER;
+               start ++;
+       } else if (*start == '<' || *start == '-') {
+               *range = NUM_LESS;
+               start ++;
+       } else
+               *range = NUM_EQ;
+
+       errno = 0; /* To enable failure after call to be determined */
+       number = strtoll(start, &end, 10);
+
+       if((errno == ERANGE && (number == LLONG_MAX || number == LLONG_MIN))
+                               || (errno != 0 && number == 0)) {
+               /* long long underflow or overflow in conversion, or other
+                * conversion error.
+                * Note: we don't check for LLONG_MIN and LLONG_MAX only
+                * because strtoll can validly return that if the
+                * user used these values
+                */
+               *error = "Long long underflow, overflow or other conversion "
+                                                               "error";
+               return 0;
+       }
+
+       if (end == start) {
+               /* Couldn't read any number  */
+               *error = "Number expected";
+               return 0;
+       }
+
+       switch (end[0]) {
+       case 'g':
+       case 'G':
+               number *= 1024;
+       case 'm':
+       case 'M':
+               number *= 1024;
+       case 'k':
+       case 'K':
+               number *= 1024;
+
+               if (end[1] != '\0') {
+                       *error = "Trailing junk after size specifier";
+                       return 0;
+               }
+
+               break;
+       case '\0':
+               break;
+       default:
+               *error = "Trailing junk after number";
+               return 0;
+       }
+
+       *size = number;
+
+       return 1;
+}
+
+
+static int parse_number_arg(struct test_entry *test, struct atom *atom)
+{
+       struct test_number_arg *number;
+       long long size;
+       int range;
+       char *error;
+       int res = parse_number(atom->argv[0], &size, &range, &error);
+
+       if (res == 0) {
+               TEST_SYNTAX_ERROR(test, 0, "%s\n", error);
+               return 0;
+       }
+
+       number = malloc(sizeof(*number));
+       if (number == NULL)
+               MEM_ERROR();
+
+       number->range = range;
+       number->size = size;
+
+       atom->data = number;
+
+       return 1;
+}
+
+
+static int parse_range_args(struct test_entry *test, struct atom *atom)
+{
+       struct test_range_args *range;
+       long long start, end;
+       int type;
+       int res;
+       char *error;
+
+       res = parse_number(atom->argv[0], &start, &type, &error);
+       if (res == 0) {
+               TEST_SYNTAX_ERROR(test, 0, "%s\n", error);
+               return 0;
+       }
+
+       if (type != NUM_EQ) {
+               TEST_SYNTAX_ERROR(test, 0, "Range specifier (<, >, -, +) not "
+                       "expected\n");
+               return 0;
+       }
+       res = parse_number(atom->argv[1], &end, &type, &error);
+       if (res == 0) {
+               TEST_SYNTAX_ERROR(test, 1, "%s\n", error);
+               return 0;
+       }
+
+       if (type != NUM_EQ) {
+               TEST_SYNTAX_ERROR(test, 1, "Range specifier (<, >, -, +) not "
+                       "expected\n");
+               return 0;
+       }
+       range = malloc(sizeof(*range));
+       if (range == NULL)
+               MEM_ERROR();
+
+       range->start = start;
+       range->end = end;
+
+       atom->data = range;
+
+       return 1;
+}
+
+
+/*
+ * Generic test code macro
+ */
+#define TEST_FN(NAME, MATCH, CODE) \
+static int NAME##_fn(struct atom *atom, struct action_data *action_data) \
+{ \
+       /* test operates on MATCH file types only */ \
+       if (!file_type_match(action_data->buf->st_mode, MATCH)) \
+               return 0; \
+ \
+       CODE \
+}
+
+/*
+ * Generic test code macro testing VAR for size (eq, less than, greater than)
+ */
+#define TEST_VAR_FN(NAME, MATCH, VAR) TEST_FN(NAME, MATCH, \
+       { \
+       int match = 0; \
+       struct test_number_arg *number = atom->data; \
+       \
+       switch (number->range) { \
+       case NUM_EQ: \
+               match = VAR == number->size; \
+               break; \
+       case NUM_LESS: \
+               match = VAR < number->size; \
+               break; \
+       case NUM_GREATER: \
+               match = VAR > number->size; \
+               break; \
+       } \
+       \
+       return match; \
+       })      
+
+
+/*
+ * Generic test code macro testing VAR for range [x, y] (value between x and y
+ * inclusive).
+ */    
+#define TEST_VAR_RANGE_FN(NAME, MATCH, VAR) TEST_FN(NAME##_range, MATCH, \
+       { \
+       struct test_range_args *range = atom->data; \
+       \
+       return range->start <= VAR && VAR <= range->end; \
+       })      
+
+
+/*
+ * Name, Pathname and Subpathname test specific code
+ */
+
+/*
+ * Add a leading "/" if subpathname and pathname lacks it
+ */
+static int check_pathname(struct test_entry *test, struct atom *atom)
+{
+       int res;
+       char *name;
+
+       if(atom->argv[0][0] != '/') {
+               res = asprintf(&name, "/%s", atom->argv[0]);
+               if(res == -1)
+                       BAD_ERROR("asprintf failed in check_pathname\n");
+
+               free(atom->argv[0]);
+               atom->argv[0] = name;
+       }
+
+       return 1;
+}
+
+
+TEST_FN(name, ACTION_ALL_LNK, \
+       return fnmatch(atom->argv[0], action_data->name,
+                               FNM_PATHNAME|FNM_PERIOD|FNM_EXTMATCH) == 0;)
+
+TEST_FN(pathname, ACTION_ALL_LNK, \
+       return fnmatch(atom->argv[0], action_data->subpath,
+                               FNM_PATHNAME|FNM_PERIOD|FNM_EXTMATCH) == 0;)
+
+
+static int count_components(char *path)
+{
+       int count;
+
+       for (count = 0; *path != '\0'; count ++) {
+               while (*path == '/')
+                       path ++;
+
+               while (*path != '\0' && *path != '/')
+                       path ++;
+       }
+
+       return count;
+}
+
+
+static char *get_start(char *s, int n)
+{
+       int count;
+       char *path = s;
+
+       for (count = 0; *path != '\0' && count < n; count ++) {
+               while (*path == '/')
+                       path ++;
+
+               while (*path != '\0' && *path != '/')
+                       path ++;
+       }
+
+       if (count == n)
+               *path = '\0';
+
+       return s;
+}
+       
+
+static int subpathname_fn(struct atom *atom, struct action_data *action_data)
+{
+       return fnmatch(atom->argv[0], get_start(strdupa(action_data->subpath),
+               count_components(atom->argv[0])),
+               FNM_PATHNAME|FNM_PERIOD|FNM_EXTMATCH) == 0;
+}
+
+/*
+ * Inode attribute test operations using generic
+ * TEST_VAR_FN(test name, file scope, attribute name) macro.
+ * This is for tests that do not need to be specially handled in any way.
+ * They just take a variable and compare it against a number.
+ */
+TEST_VAR_FN(filesize, ACTION_REG, action_data->buf->st_size)
+
+TEST_VAR_FN(dirsize, ACTION_DIR, action_data->buf->st_size)
+
+TEST_VAR_FN(size, ACTION_ALL_LNK, action_data->buf->st_size)
+
+TEST_VAR_FN(inode, ACTION_ALL_LNK, action_data->buf->st_ino)
+
+TEST_VAR_FN(nlink, ACTION_ALL_LNK, action_data->buf->st_nlink)
+
+TEST_VAR_FN(fileblocks, ACTION_REG, action_data->buf->st_blocks)
+
+TEST_VAR_FN(dirblocks, ACTION_DIR, action_data->buf->st_blocks)
+
+TEST_VAR_FN(blocks, ACTION_ALL_LNK, action_data->buf->st_blocks)
+
+TEST_VAR_FN(dircount, ACTION_DIR, action_data->dir_ent->dir->count)
+
+TEST_VAR_FN(depth, ACTION_ALL_LNK, action_data->depth)
+
+TEST_VAR_RANGE_FN(filesize, ACTION_REG, action_data->buf->st_size)
+
+TEST_VAR_RANGE_FN(dirsize, ACTION_DIR, action_data->buf->st_size)
+
+TEST_VAR_RANGE_FN(size, ACTION_ALL_LNK, action_data->buf->st_size)
+
+TEST_VAR_RANGE_FN(inode, ACTION_ALL_LNK, action_data->buf->st_ino)
+
+TEST_VAR_RANGE_FN(nlink, ACTION_ALL_LNK, action_data->buf->st_nlink)
+
+TEST_VAR_RANGE_FN(fileblocks, ACTION_REG, action_data->buf->st_blocks)
+
+TEST_VAR_RANGE_FN(dirblocks, ACTION_DIR, action_data->buf->st_blocks)
+
+TEST_VAR_RANGE_FN(blocks, ACTION_ALL_LNK, action_data->buf->st_blocks)
+
+TEST_VAR_RANGE_FN(gid, ACTION_ALL_LNK, action_data->buf->st_gid)
+
+TEST_VAR_RANGE_FN(uid, ACTION_ALL_LNK, action_data->buf->st_uid)
+
+TEST_VAR_RANGE_FN(depth, ACTION_ALL_LNK, action_data->depth)
+
+TEST_VAR_RANGE_FN(dircount, ACTION_DIR, action_data->dir_ent->dir->count)
+
+/*
+ * uid specific test code
+ */
+TEST_VAR_FN(uid, ACTION_ALL_LNK, action_data->buf->st_uid)
+
+static int parse_uid_arg(struct test_entry *test, struct atom *atom)
+{
+       struct test_number_arg *number;
+       long long size;
+       int range;
+       char *error;
+
+       if(parse_number(atom->argv[0], &size, &range, &error)) {
+               /* managed to fully parse argument as a number */
+               if(size < 0 || size > (((long long) 1 << 32) - 1)) {
+                       TEST_SYNTAX_ERROR(test, 1, "Numeric uid out of "
+                                                               "range\n");
+                       return 0;
+               }
+       } else {
+               /* couldn't parse (fully) as a number, is it a user name? */
+               struct passwd *uid = getpwnam(atom->argv[0]);
+               if(uid) {
+                       size = uid->pw_uid;
+                       range = NUM_EQ;
+               } else {
+                       TEST_SYNTAX_ERROR(test, 1, "Invalid uid or unknown "
+                                                               "user\n");
+                       return 0;
+               }
+       }
+
+       number = malloc(sizeof(*number));
+       if(number == NULL)
+               MEM_ERROR();
+
+       number->range = range;
+       number->size= size;
+
+       atom->data = number;
+
+       return 1;
+}
+
+
+/*
+ * gid specific test code
+ */
+TEST_VAR_FN(gid, ACTION_ALL_LNK, action_data->buf->st_gid)
+
+static int parse_gid_arg(struct test_entry *test, struct atom *atom)
+{
+       struct test_number_arg *number;
+       long long size;
+       int range;
+       char *error;
+
+       if(parse_number(atom->argv[0], &size, &range, &error)) {
+               /* managed to fully parse argument as a number */
+               if(size < 0 || size > (((long long) 1 << 32) - 1)) {
+                       TEST_SYNTAX_ERROR(test, 1, "Numeric gid out of "
+                                                               "range\n");
+                       return 0;
+               }
+       } else {
+               /* couldn't parse (fully) as a number, is it a group name? */
+               struct group *gid = getgrnam(atom->argv[0]);
+               if(gid) {
+                       size = gid->gr_gid;
+                       range = NUM_EQ;
+               } else {
+                       TEST_SYNTAX_ERROR(test, 1, "Invalid gid or unknown "
+                                                               "group\n");
+                       return 0;
+               }
+       }
+
+       number = malloc(sizeof(*number));
+       if(number == NULL)
+               MEM_ERROR();
+
+       number->range = range;
+       number->size= size;
+
+       atom->data = number;
+
+       return 1;
+}
+
+
+/*
+ * Type test specific code
+ */
+struct type_entry type_table[] = {
+       { S_IFSOCK, 's' },
+       { S_IFLNK, 'l' },
+       { S_IFREG, 'f' },
+       { S_IFBLK, 'b' },
+       { S_IFDIR, 'd' },
+       { S_IFCHR, 'c' },
+       { S_IFIFO, 'p' },
+       { 0, 0 },
+};
+
+
+static int parse_type_arg(struct test_entry *test, struct atom *atom)
+{
+       int i;
+
+       if (strlen(atom->argv[0]) != 1)
+               goto failed;
+
+       for(i = 0; type_table[i].type != 0; i++)
+               if (type_table[i].type == atom->argv[0][0])
+                       break;
+
+       atom->data = &type_table[i];
+
+       if(type_table[i].type != 0)
+               return 1;
+
+failed:
+       TEST_SYNTAX_ERROR(test, 0, "Unexpected file type, expected 'f', 'd', "
+               "'c', 'b', 'l', 's' or 'p'\n");
+       return 0;
+}
+       
+
+static int type_fn(struct atom *atom, struct action_data *action_data)
+{
+       struct type_entry *type = atom->data;
+
+       return (action_data->buf->st_mode & S_IFMT) == type->value;
+}
+
+
+/*
+ * True test specific code
+ */
+static int true_fn(struct atom *atom, struct action_data *action_data)
+{
+       return 1;
+}
+
+
+/*
+ *  False test specific code
+ */
+static int false_fn(struct atom *atom, struct action_data *action_data)
+{
+       return 0;
+}
+
+
+/*
+ *  File test specific code
+ */
+static int parse_file_arg(struct test_entry *test, struct atom *atom)
+{
+       int res;
+       regex_t *preg = malloc(sizeof(regex_t));
+
+       if (preg == NULL)
+               MEM_ERROR();
+
+       res = regcomp(preg, atom->argv[0], REG_EXTENDED);
+       if (res) {
+               char str[1024]; /* overflow safe */
+
+               regerror(res, preg, str, 1024);
+               free(preg);
+               TEST_SYNTAX_ERROR(test, 0, "invalid regex \"%s\" because "
+                       "\"%s\"\n", atom->argv[0], str);
+               return 0;
+       }
+
+       atom->data = preg;
+
+       return 1;
+}
+
+
+static int file_fn(struct atom *atom, struct action_data *action_data)
+{
+       int child, res, size = 0, status;
+       int pipefd[2];
+       char *buffer = NULL;
+       regex_t *preg = atom->data;
+
+       res = pipe(pipefd);
+       if (res == -1)
+               BAD_ERROR("file_fn pipe failed\n");
+
+       child = fork();
+       if (child == -1)
+               BAD_ERROR("file_fn fork_failed\n");
+
+       if (child == 0) {
+               /*
+                * Child process
+                * Connect stdout to pipefd[1] and execute file command
+                */
+               close(STDOUT_FILENO);
+               res = dup(pipefd[1]);
+               if (res == -1)
+                       exit(EXIT_FAILURE);
+
+               execlp("file", "file", "-b", action_data->pathname,
+                       (char *) NULL);
+               exit(EXIT_FAILURE);
+       }
+
+       /*
+        * Parent process.  Read stdout from file command
+        */
+       close(pipefd[1]);
+
+       do {
+               buffer = realloc(buffer, size + 512);
+               if (buffer == NULL)
+                       MEM_ERROR();
+
+               res = read_bytes(pipefd[0], buffer + size, 512);
+
+               if (res == -1)
+                       BAD_ERROR("file_fn pipe read error\n");
+
+               size += 512;
+
+       } while (res == 512);
+
+       size = size + res - 512;
+
+       buffer[size] = '\0';
+
+       res = waitpid(child,  &status, 0);
+
+       if (res == -1)
+               BAD_ERROR("file_fn waitpid failed\n");
+       if (!WIFEXITED(status) || WEXITSTATUS(status) != 0)
+               BAD_ERROR("file_fn file returned error\n");
+
+       close(pipefd[0]);
+
+       res = regexec(preg, buffer, (size_t) 0, NULL, 0);
+
+       free(buffer);
+
+       return res == 0;
+}
+
+
+/*
+ *  Exec test specific code
+ */
+static int exec_fn(struct atom *atom, struct action_data *action_data)
+{
+       int child, i, res, status;
+
+       child = fork();
+       if (child == -1)
+               BAD_ERROR("exec_fn fork_failed\n");
+
+       if (child == 0) {
+               /*
+                * Child process
+                * redirect stdin, stdout & stderr to /dev/null and
+                * execute atom->argv[0]
+                */
+               int fd = open("/dev/null", O_RDWR);
+               if(fd == -1)
+                       exit(EXIT_FAILURE);
+
+               close(STDIN_FILENO);
+               close(STDOUT_FILENO);
+               close(STDERR_FILENO);
+               for(i = 0; i < 3; i++) {
+                       res = dup(fd);
+                       if (res == -1)
+                               exit(EXIT_FAILURE);
+               }
+               close(fd);
+
+               /*
+                * Create environment variables
+                * NAME: name of file
+                * PATHNAME: pathname of file relative to squashfs root
+                * SOURCE_PATHNAME: the pathname of the file in the source
+                *                  directory
+                */
+               res = setenv("NAME", action_data->name, 1);
+               if(res == -1)
+                       exit(EXIT_FAILURE);
+
+               res = setenv("PATHNAME", action_data->subpath, 1);
+               if(res == -1)
+                       exit(EXIT_FAILURE);
+
+               res = setenv("SOURCE_PATHNAME", action_data->pathname, 1);
+               if(res == -1)
+                       exit(EXIT_FAILURE);
+
+               execl("/bin/sh", "sh", "-c", atom->argv[0], (char *) NULL);
+               exit(EXIT_FAILURE);
+       }
+
+       /*
+        * Parent process. 
+        */
+
+       res = waitpid(child,  &status, 0);
+
+       if (res == -1)
+               BAD_ERROR("exec_fn waitpid failed\n");
+       return WIFEXITED(status) ? WEXITSTATUS(status) == 0 : 0;
+}
+
+
+/*
+ * Symbolic link specific test code
+ */
+
+/*
+ * Walk the supplied pathname and return the directory entry corresponding
+ * to the pathname.  If any symlinks are encountered whilst walking the
+ * pathname, then recursively walk these, to obtain the fully
+ * dereferenced canonicalised directory entry.
+ *
+ * If follow_path fails to walk a pathname either because a component
+ * doesn't exist, it is a non directory component when a directory
+ * component is expected, a symlink with an absolute path is encountered,
+ * or a symlink is encountered which cannot be recursively walked due to
+ * the above failures, then return NULL.
+ */
+static struct dir_ent *follow_path(struct dir_info *dir, char *pathname)
+{
+       char *comp, *path = pathname;
+       struct dir_ent *dir_ent = NULL;
+
+       /* We cannot follow absolute paths */
+       if(pathname[0] == '/')
+               return NULL;
+
+       for(comp = get_comp(&path); comp; free(comp), comp = get_comp(&path)) {
+               if(strcmp(comp, ".") == 0)
+                       continue;
+
+               if(strcmp(comp, "..") == 0) {
+                       /* Move to parent if we're not in the root directory */
+                       if(dir->depth > 1) {
+                               dir = dir->dir_ent->our_dir;
+                               dir_ent = NULL; /* lazily eval at loop exit */
+                               continue;
+                       } else
+                               /* Failed to walk pathname */
+                               return NULL;
+               }
+
+               /* Lookup comp in current directory */
+               dir_ent = lookup_comp(comp, dir);
+               if(dir_ent == NULL)
+                       /* Doesn't exist, failed to walk pathname */
+                       return NULL;
+
+               if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFLNK) {
+                       /* Symbolic link, try to walk it */
+                       dir_ent = follow_path(dir, dir_ent->inode->symlink);
+                       if(dir_ent == NULL)
+                               /* Failed to follow symlink */
+                               return NULL;
+               }
+
+               if((dir_ent->inode->buf.st_mode & S_IFMT) != S_IFDIR)
+                       /* Cannot walk further */
+                       break;
+
+               dir = dir_ent->dir;
+       }
+
+       /* We will have exited the loop either because we've processed
+        * all the components, which means we've successfully walked the
+        * pathname, or because we've hit a non-directory, in which case
+        * it's success if this is the leaf component */
+       if(comp) {
+               free(comp);
+               comp = get_comp(&path);
+               free(comp);
+               if(comp != NULL)
+                       /* Not a leaf component */
+                       return NULL;
+       } else {
+               /* Fully walked pathname, dir_ent contains correct value unless
+                * we've walked to the parent ("..") in which case we need
+                * to resolve it here */
+               if(!dir_ent)
+                       dir_ent = dir->dir_ent;
+       }
+
+       return dir_ent;
+}
+
+
+static int exists_fn(struct atom *atom, struct action_data *action_data)
+{
+       /*
+        * Test if a symlink exists within the output filesystem, that is,
+        * the symlink has a relative path, and the relative path refers
+        * to an entry within the output filesystem.
+        *
+        * This test function evaluates the path for symlinks - that is it
+        * follows any symlinks in the path (and any symlinks that it contains
+        * etc.), to discover the fully dereferenced canonicalised relative
+        * path.
+        *
+        * If any symlinks within the path do not exist or are absolute
+        * then the symlink is considered to not exist, as it cannot be
+        * fully dereferenced.
+        *
+        * exists operates on symlinks only, other files by definition
+        * exist
+        */
+       if (!file_type_match(action_data->buf->st_mode, ACTION_LNK))
+               return 1;
+
+       /* dereference the symlink, and return TRUE if it exists */
+       return follow_path(action_data->dir_ent->our_dir,
+                       action_data->dir_ent->inode->symlink) ? 1 : 0;
+}
+
+
+static int absolute_fn(struct atom *atom, struct action_data *action_data)
+{
+       /*
+        * Test if a symlink has an absolute path, which by definition
+        * means the symbolic link may be broken (even if the absolute path
+        * does point into the filesystem being squashed, because the resultant
+        * filesystem can be mounted/unsquashed anywhere, it is unlikely the
+        * absolute path will still point to the right place).  If you know that
+        * an absolute symlink will point to the right place then you don't need
+        * to use this function, and/or these symlinks can be excluded by
+        * use of other test operators.
+        *
+        * absolute operates on symlinks only, other files by definition
+        * don't have problems
+        */
+       if (!file_type_match(action_data->buf->st_mode, ACTION_LNK))
+               return 0;
+
+       return action_data->dir_ent->inode->symlink[0] == '/';
+}
+
+
+static int parse_expr_argX(struct test_entry *test, struct atom *atom,
+       int argno)
+{
+       /* Call parse_expr to parse argument, which should be an expression */
+
+        /* save the current parser state */
+       char *save_cur_ptr = cur_ptr;
+       char *save_source = source;
+
+       cur_ptr = source = atom->argv[argno];
+       atom->data = parse_expr(0);
+
+       cur_ptr = save_cur_ptr;
+       source = save_source;
+
+       if(atom->data == NULL) {
+               /* parse_expr(0) will have reported the exact syntax error,
+                * but, because we recursively evaluated the expression, it
+                * will have been reported without the context of the stat
+                * test().  So here additionally report our failure to parse
+                * the expression in the stat() test to give context */
+               TEST_SYNTAX_ERROR(test, 0, "Failed to parse expression\n");
+               return 0;
+       }
+
+       return 1;
+}
+
+
+static int parse_expr_arg0(struct test_entry *test, struct atom *atom)
+{
+       return parse_expr_argX(test, atom, 0);
+}
+
+
+static int parse_expr_arg1(struct test_entry *test, struct atom *atom)
+{
+       return parse_expr_argX(test, atom, 1);
+}
+
+
+static int stat_fn(struct atom *atom, struct action_data *action_data)
+{
+       struct stat buf;
+       struct action_data eval_action;
+       int match, res;
+
+       /* evaluate the expression using the context of the inode
+        * pointed to by the symlink.  This allows the inode attributes
+        * of the file pointed to by the symlink to be evaluated, rather
+        * than the symlink itself.
+        *
+        * Note, stat() deliberately does not evaluate the pathname, name or
+        * depth of the symlink, these are left with the symlink values.
+        * This allows stat() to be used on any symlink, rather than
+        * just symlinks which are contained (if the symlink is *not*
+        * contained then pathname, name and depth are meaningless as they
+        * are relative to the filesystem being squashed). */
+
+       /* if this isn't a symlink then stat will just return the current
+        * information, i.e. stat(expr) == expr.  This is harmless and
+        * is better than returning TRUE or FALSE in a non symlink case */
+       res = stat(action_data->pathname, &buf);
+       if(res == -1) {
+               if(expr_log_cmnd(LOG_ENABLED)) {
+                       expr_log(atom->test->name);
+                       expr_log("(");
+                       expr_log_match(0);
+                       expr_log(")");
+               }
+               return 0;
+       }
+
+       /* fill in the inode values of the file pointed to by the
+        * symlink, but, leave everything else the same */
+       memcpy(&eval_action, action_data, sizeof(struct action_data));
+       eval_action.buf = &buf;
+
+       if(expr_log_cmnd(LOG_ENABLED)) {
+               expr_log(atom->test->name);
+               expr_log("(");
+               match = eval_expr_log(atom->data, &eval_action);
+               expr_log(")");
+       } else
+               match = eval_expr(atom->data, &eval_action);
+
+       return match;
+}
+
+
+static int readlink_fn(struct atom *atom, struct action_data *action_data)
+{
+       int match = 0;
+       struct dir_ent *dir_ent;
+       struct action_data eval_action;
+
+       /* Dereference the symlink and evaluate the expression in the
+        * context of the file pointed to by the symlink.
+        * All attributes are updated to refer to the file that is pointed to.
+        * Thus the inode attributes, pathname, name and depth all refer to
+        * the dereferenced file, and not the symlink.
+        *
+        * If the symlink cannot be dereferenced because it doesn't exist in
+        * the output filesystem, or due to some other failure to
+        * walk the pathname (see follow_path above), then FALSE is returned.
+        *
+        * If you wish to evaluate the inode attributes of symlinks which
+        * exist in the source filestem (but not in the output filesystem then
+        * use stat instead (see above).
+        *
+        * readlink operates on symlinks only */
+       if (!file_type_match(action_data->buf->st_mode, ACTION_LNK))
+               goto finish;
+
+       /* dereference the symlink, and get the directory entry it points to */
+       dir_ent = follow_path(action_data->dir_ent->our_dir,
+                       action_data->dir_ent->inode->symlink);
+       if(dir_ent == NULL)
+               goto finish;
+
+       eval_action.name = dir_ent->name;
+       eval_action.pathname = strdup(pathname(dir_ent));
+       eval_action.subpath = strdup(subpathname(dir_ent));
+       eval_action.buf = &dir_ent->inode->buf;
+       eval_action.depth = dir_ent->our_dir->depth;
+       eval_action.dir_ent = dir_ent;
+       eval_action.root = action_data->root;
+
+       if(expr_log_cmnd(LOG_ENABLED)) {
+               expr_log(atom->test->name);
+               expr_log("(");
+               match = eval_expr_log(atom->data, &eval_action);
+               expr_log(")");
+       } else
+               match = eval_expr(atom->data, &eval_action);
+
+       free(eval_action.pathname);
+       free(eval_action.subpath);
+
+       return match;
+
+finish:
+       if(expr_log_cmnd(LOG_ENABLED)) {
+               expr_log(atom->test->name);
+               expr_log("(");
+               expr_log_match(0);
+               expr_log(")");
+       }
+
+       return 0;
+}
+
+
+static int eval_fn(struct atom *atom, struct action_data *action_data)
+{
+       int match;
+       char *path = atom->argv[0];
+       struct dir_ent *dir_ent = action_data->dir_ent;
+       struct stat *buf = action_data->buf;
+       struct action_data eval_action;
+
+       /* Follow path (arg1) and evaluate the expression (arg2)
+        * in the context of the file discovered.  All attributes are updated
+        * to refer to the file that is pointed to.
+        *
+        * This test operation allows you to add additional context to the
+        * evaluation of the file being scanned, such as "if current file is
+        * XXX and the parent is YYY, then ..."  Often times you need or
+        * want to test a combination of file status
+        *
+        * If the file referenced by the path does not exist in
+        * the output filesystem, or some other failure is experienced in
+        * walking the path (see follow_path above), then FALSE is returned.
+        *
+        * If you wish to evaluate the inode attributes of files which
+        * exist in the source filestem (but not in the output filesystem then
+        * use stat instead (see above). */
+
+       /* try to follow path, and get the directory entry it points to */
+       if(path[0] == '/') {
+               /* absolute, walk from root - first skip the leading / */
+               while(path[0] == '/')
+                       path ++;
+               if(path[0] == '\0')
+                       dir_ent = action_data->root->dir_ent;
+               else
+                       dir_ent = follow_path(action_data->root, path);
+       } else {
+               /* relative, if first component is ".." walk from parent,
+                * otherwise walk from dir_ent.
+                * Note: this has to be handled here because follow_path
+                * will quite correctly refuse to execute ".." on anything
+                * which isn't a directory */
+               if(strncmp(path, "..", 2) == 0 && (path[2] == '\0' ||
+                                                       path[2] == '/')) {
+                       /* walk from parent */
+                       path += 2;
+                       while(path[0] == '/')
+                               path ++;
+                       if(path[0] == '\0')
+                               dir_ent = dir_ent->our_dir->dir_ent;
+                       else 
+                               dir_ent = follow_path(dir_ent->our_dir, path);
+               } else if(!file_type_match(buf->st_mode, ACTION_DIR))
+                       dir_ent = NULL;
+               else
+                       dir_ent = follow_path(dir_ent->dir, path);
+       }
+
+       if(dir_ent == NULL) {
+               if(expr_log_cmnd(LOG_ENABLED)) {
+                       expr_log(atom->test->name);
+                       expr_log("(");
+                       expr_log(atom->argv[0]);
+                       expr_log(",");
+                       expr_log_match(0);
+                       expr_log(")");
+               }
+
+               return 0;
+       }
+
+       eval_action.name = dir_ent->name;
+       eval_action.pathname = strdup(pathname(dir_ent));
+       eval_action.subpath = strdup(subpathname(dir_ent));
+       eval_action.buf = &dir_ent->inode->buf;
+       eval_action.depth = dir_ent->our_dir->depth;
+       eval_action.dir_ent = dir_ent;
+       eval_action.root = action_data->root;
+
+       if(expr_log_cmnd(LOG_ENABLED)) {
+               expr_log(atom->test->name);
+               expr_log("(");
+               expr_log(eval_action.subpath);
+               expr_log(",");
+               match = eval_expr_log(atom->data, &eval_action);
+               expr_log(")");
+       } else
+               match = eval_expr(atom->data, &eval_action);
+
+       free(eval_action.pathname);
+       free(eval_action.subpath);
+
+       return match;
+}
+
+
+/*
+ * Perm specific test code
+ */
+static int parse_perm_args(struct test_entry *test, struct atom *atom)
+{
+       int res = 1, mode, op, i;
+       char *arg;
+       struct mode_data *head = NULL, *cur = NULL;
+       struct perm_data *perm_data;
+
+       if(atom->args == 0) {
+               TEST_SYNTAX_ERROR(test, 0, "One or more arguments expected\n");
+               return 0;
+       }
+
+       switch(atom->argv[0][0]) {
+       case '-':
+               op = PERM_ALL;
+               arg = atom->argv[0] + 1;
+               break;
+       case '/':
+               op = PERM_ANY;
+               arg = atom->argv[0] + 1;
+               break;
+       default:
+               op = PERM_EXACT;
+               arg = atom->argv[0];
+               break;
+       }
+
+       /* try to parse as an octal number */
+       res = parse_octal_mode_args(atom->args, atom->argv, (void **) &head);
+       if(res == -1) {
+               /* parse as sym mode argument */
+               for(i = 0; i < atom->args && res; i++, arg = atom->argv[i])
+                       res = parse_sym_mode_arg(arg, &head, &cur);
+       }
+
+       if (res == 0)
+               goto finish;
+
+       /*
+        * Evaluate the symbolic mode against a permission of 0000 octal
+        */
+       mode = mode_execute(head, 0);
+
+       perm_data = malloc(sizeof(struct perm_data));
+       if (perm_data == NULL)
+               MEM_ERROR();
+
+       perm_data->op = op;
+       perm_data->mode = mode;
+
+       atom->data = perm_data;
+       
+finish:
+       while(head) {
+               struct mode_data *tmp = head;
+               head = head->next;
+               free(tmp);
+       }
+
+       return res;
+}
+
+
+static int perm_fn(struct atom *atom, struct action_data *action_data)
+{
+       struct perm_data *perm_data = atom->data;
+       struct stat *buf = action_data->buf;
+
+       switch(perm_data->op) {
+       case PERM_EXACT:
+               return (buf->st_mode & ~S_IFMT) == perm_data->mode;
+       case PERM_ALL:
+               return (buf->st_mode & perm_data->mode) == perm_data->mode;
+       case PERM_ANY:
+       default:
+               /*
+                * if no permission bits are set in perm_data->mode match
+                * on any file, this is to be consistent with find, which
+                * does this to be consistent with the behaviour of
+                * -perm -000
+                */
+               return perm_data->mode == 0 || (buf->st_mode & perm_data->mode);
+       }
+}
+
+
+#ifdef SQUASHFS_TRACE
+static void dump_parse_tree(struct expr *expr)
+{
+       int i;
+
+       if(expr->type == ATOM_TYPE) {
+               printf("%s", expr->atom.test->name);
+               if(expr->atom.args) {
+                       printf("(");
+                       for(i = 0; i < expr->atom.args; i++) {
+                               printf("%s", expr->atom.argv[i]);
+                               if (i + 1 < expr->atom.args)
+                                       printf(",");
+                       }
+                       printf(")");
+               }
+       } else if (expr->type == UNARY_TYPE) {
+               printf("%s", token_table[expr->unary_op.op].string);
+               dump_parse_tree(expr->unary_op.expr);
+       } else {
+               printf("(");
+               dump_parse_tree(expr->expr_op.lhs);
+               printf("%s", token_table[expr->expr_op.op].string);
+               dump_parse_tree(expr->expr_op.rhs);
+               printf(")");
+       }
+}
+
+
+void dump_action_list(struct action *spec_list, int spec_count)
+{
+       int i;
+
+       for (i = 0; i < spec_count; i++) {
+               printf("%s", spec_list[i].action->name);
+               if (spec_list[i].args) {
+                       int n;
+
+                       printf("(");
+                       for (n = 0; n < spec_list[i].args; n++) {
+                               printf("%s", spec_list[i].argv[n]);
+                               if (n + 1 < spec_list[i].args)
+                                       printf(",");
+                       }
+                       printf(")");
+               }
+               printf("=");
+               dump_parse_tree(spec_list[i].expr);
+               printf("\n");
+       }
+}
+
+
+void dump_actions()
+{
+       dump_action_list(exclude_spec, exclude_count);
+       dump_action_list(fragment_spec, fragment_count);
+       dump_action_list(other_spec, other_count);
+       dump_action_list(move_spec, move_count);
+       dump_action_list(empty_spec, empty_count);
+}
+#else
+void dump_actions()
+{
+}
+#endif
+
+
+static struct test_entry test_table[] = {
+       { "name", 1, name_fn, NULL, 1},
+       { "pathname", 1, pathname_fn, check_pathname, 1, 0},
+       { "subpathname", 1, subpathname_fn, check_pathname, 1, 0},
+       { "filesize", 1, filesize_fn, parse_number_arg, 1, 0},
+       { "dirsize", 1, dirsize_fn, parse_number_arg, 1, 0},
+       { "size", 1, size_fn, parse_number_arg, 1, 0},
+       { "inode", 1, inode_fn, parse_number_arg, 1, 0},
+       { "nlink", 1, nlink_fn, parse_number_arg, 1, 0},
+       { "fileblocks", 1, fileblocks_fn, parse_number_arg, 1, 0},
+       { "dirblocks", 1, dirblocks_fn, parse_number_arg, 1, 0},
+       { "blocks", 1, blocks_fn, parse_number_arg, 1, 0},
+       { "gid", 1, gid_fn, parse_gid_arg, 1, 0},
+       { "uid", 1, uid_fn, parse_uid_arg, 1, 0},
+       { "depth", 1, depth_fn, parse_number_arg, 1, 0},
+       { "dircount", 1, dircount_fn, parse_number_arg, 0, 0},
+       { "filesize_range", 2, filesize_range_fn, parse_range_args, 1, 0},
+       { "dirsize_range", 2, dirsize_range_fn, parse_range_args, 1, 0},
+       { "size_range", 2, size_range_fn, parse_range_args, 1, 0},
+       { "inode_range", 2, inode_range_fn, parse_range_args, 1, 0},
+       { "nlink_range", 2, nlink_range_fn, parse_range_args, 1, 0},
+       { "fileblocks_range", 2, fileblocks_range_fn, parse_range_args, 1, 0},
+       { "dirblocks_range", 2, dirblocks_range_fn, parse_range_args, 1, 0},
+       { "blocks_range", 2, blocks_range_fn, parse_range_args, 1, 0},
+       { "gid_range", 2, gid_range_fn, parse_range_args, 1, 0},
+       { "uid_range", 2, uid_range_fn, parse_range_args, 1, 0},
+       { "depth_range", 2, depth_range_fn, parse_range_args, 1, 0},
+       { "dircount_range", 2, dircount_range_fn, parse_range_args, 0, 0},
+       { "type", 1, type_fn, parse_type_arg, 1, 0},
+       { "true", 0, true_fn, NULL, 1, 0},
+       { "false", 0, false_fn, NULL, 1, 0},
+       { "file", 1, file_fn, parse_file_arg, 1, 0},
+       { "exec", 1, exec_fn, NULL, 1, 0},
+       { "exists", 0, exists_fn, NULL, 0, 0},
+       { "absolute", 0, absolute_fn, NULL, 0, 0},
+       { "stat", 1, stat_fn, parse_expr_arg0, 1, 1},
+       { "readlink", 1, readlink_fn, parse_expr_arg0, 0, 1},
+       { "eval", 2, eval_fn, parse_expr_arg1, 0, 1},
+       { "perm", -2, perm_fn, parse_perm_args, 1, 0},
+       { "", -1 }
+};
+
+
+static struct action_entry action_table[] = {
+       { "fragment", FRAGMENT_ACTION, 1, ACTION_REG, NULL, NULL},
+       { "exclude", EXCLUDE_ACTION, 0, ACTION_ALL_LNK, NULL, NULL},
+       { "fragments", FRAGMENTS_ACTION, 0, ACTION_REG, NULL, frag_action},
+       { "no-fragments", NO_FRAGMENTS_ACTION, 0, ACTION_REG, NULL,
+                                               no_frag_action},
+       { "always-use-fragments", ALWAYS_FRAGS_ACTION, 0, ACTION_REG, NULL,
+                                               always_frag_action},
+       { "dont-always-use-fragments", NO_ALWAYS_FRAGS_ACTION, 0, ACTION_REG,   
+                                               NULL, no_always_frag_action},
+       { "compressed", COMPRESSED_ACTION, 0, ACTION_REG, NULL, comp_action},
+       { "uncompressed", UNCOMPRESSED_ACTION, 0, ACTION_REG, NULL,
+                                               uncomp_action},
+       { "uid", UID_ACTION, 1, ACTION_ALL_LNK, parse_uid_args, uid_action},
+       { "gid", GID_ACTION, 1, ACTION_ALL_LNK, parse_gid_args, gid_action},
+       { "guid", GUID_ACTION, 2, ACTION_ALL_LNK, parse_guid_args, guid_action},
+       { "mode", MODE_ACTION, -2, ACTION_ALL, parse_mode_args, mode_action },
+       { "empty", EMPTY_ACTION, -2, ACTION_DIR, parse_empty_args, NULL},
+       { "move", MOVE_ACTION, 1, ACTION_ALL_LNK, NULL, NULL},
+       { "prune", PRUNE_ACTION, 0, ACTION_ALL_LNK, NULL, NULL},
+       { "chmod", MODE_ACTION, -2, ACTION_ALL, parse_mode_args, mode_action },
+       { "noop", NOOP_ACTION, 0, ACTION_ALL, NULL, noop_action },
+       { "", 0, -1, 0, NULL, NULL}
+};
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/action.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/action.h
new file mode 100644 (file)
index 0000000..0a8de7c
--- /dev/null
@@ -0,0 +1,328 @@
+#ifndef ACTION_H
+#define ACTION_H
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2011, 2012, 2013, 2014
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * action.h
+ */
+
+/*
+ * Lexical analyser definitions
+ */
+#define TOK_OPEN_BRACKET       0
+#define TOK_CLOSE_BRACKET      1
+#define TOK_AND                        2
+#define TOK_OR                 3
+#define TOK_NOT                        4
+#define TOK_COMMA              5
+#define TOK_AT                 6
+#define TOK_WHITE_SPACE                7
+#define TOK_STRING             8
+#define TOK_EOF                        9
+
+#define TOK_TO_STR(OP, S) ({ \
+       char *s; \
+       switch(OP) { \
+       case TOK_EOF: \
+               s = "EOF"; \
+               break; \
+       case TOK_STRING: \
+               s = S; \
+               break; \
+       default: \
+               s = token_table[OP].string; \
+               break; \
+       } \
+       s; \
+})
+
+
+struct token_entry {
+       char *string;
+       int token;
+       int size;
+};
+
+/*
+ * Expression parser definitions
+ */
+#define OP_TYPE                        0
+#define ATOM_TYPE              1
+#define UNARY_TYPE             2
+
+#define SYNTAX_ERROR(S, ARGS...) { \
+       char *src = strdup(source); \
+       src[cur_ptr - source] = '\0'; \
+       fprintf(stderr, "Failed to parse action \"%s\"\n", source); \
+       fprintf(stderr, "Syntax error: "S, ##ARGS); \
+       fprintf(stderr, "Got here \"%s\"\n", src); \
+       free(src); \
+}
+
+#define TEST_SYNTAX_ERROR(TEST, ARG, S, ARGS...) { \
+       char *src = strdup(source); \
+       src[cur_ptr - source] = '\0'; \
+       fprintf(stderr, "Failed to parse action \"%s\"\n", source); \
+       fprintf(stderr, "Syntax error in \"%s()\", arg %d: "S, TEST->name, \
+                        ARG, ##ARGS); \
+       fprintf(stderr, "Got here \"%s\"\n", src); \
+       free(src); \
+}
+
+struct expr;
+
+struct expr_op {
+       struct expr *lhs;
+       struct expr *rhs;
+       int op;
+};
+
+
+struct atom {
+       struct test_entry *test;
+       int args;
+       char **argv;
+       void *data;
+};
+
+
+struct unary_op {
+       struct expr *expr;
+       int op;
+};
+
+
+struct expr {
+       int type;
+       union {
+               struct atom atom;
+               struct expr_op expr_op;
+               struct unary_op unary_op;
+       };
+};
+
+/*
+ * Test operation definitions
+ */
+#define NUM_EQ         1
+#define NUM_LESS       2
+#define NUM_GREATER    3
+
+struct test_number_arg {
+       long long size;
+       int range;
+};
+
+struct test_range_args {
+       long long start;
+       long long end;
+};
+
+struct action;
+struct action_data;
+
+struct test_entry {
+       char *name;
+       int args;
+       int (*fn)(struct atom *, struct action_data *);
+       int (*parse_args)(struct test_entry *, struct atom *);
+       int exclude_ok;
+       int handle_logging;
+};
+
+
+/*
+ * Type test specific definitions
+ */
+struct type_entry {
+       int value;
+       char type;
+};
+
+
+/*
+ * Action definitions
+ */
+#define FRAGMENT_ACTION 0
+#define EXCLUDE_ACTION 1
+#define FRAGMENTS_ACTION 2
+#define NO_FRAGMENTS_ACTION 3
+#define ALWAYS_FRAGS_ACTION 4
+#define NO_ALWAYS_FRAGS_ACTION 5
+#define COMPRESSED_ACTION 6
+#define UNCOMPRESSED_ACTION 7
+#define UID_ACTION 8
+#define GID_ACTION 9
+#define GUID_ACTION 10
+#define MODE_ACTION 11
+#define EMPTY_ACTION 12
+#define MOVE_ACTION 13
+#define PRUNE_ACTION 14
+#define NOOP_ACTION 15
+
+/*
+ * Define what file types each action operates over
+ */
+#define ACTION_DIR 1
+#define ACTION_REG 2
+#define ACTION_ALL_LNK 3
+#define ACTION_ALL 4
+#define ACTION_LNK 5
+
+
+/*
+ * Action logging requested, specified by the various
+ * -action, -true-action, -false-action and -verbose-action
+ * options
+ */
+#define ACTION_LOG_NONE        0
+#define ACTION_LOG_TRUE 1
+#define ACTION_LOG_FALSE 2
+#define ACTION_LOG_VERBOSE ACTION_LOG_TRUE | ACTION_LOG_FALSE
+
+struct action_entry {
+       char *name;
+       int type;
+       int args;
+       int file_types;
+       int (*parse_args)(struct action_entry *, int, char **, void **);
+       void (*run_action)(struct action *, struct dir_ent *);
+};
+
+
+struct action_data {
+       int depth;
+       char *name;
+       char *pathname;
+       char *subpath;
+       struct stat *buf;
+       struct dir_ent *dir_ent;
+       struct dir_info *root;
+};
+
+
+struct action {
+       int type;
+       struct action_entry *action;
+       int args;
+       char **argv;
+       struct expr *expr;
+       void *data;
+       int verbose;
+};
+
+
+/*
+ * Uid/gid action specific definitions
+ */
+struct uid_info {
+       uid_t uid;
+};
+
+struct gid_info {
+       gid_t gid;
+};
+
+struct guid_info {
+       uid_t uid;
+       gid_t gid;
+};
+
+
+/*
+ * Mode action specific definitions
+ */
+#define ACTION_MODE_SET 0
+#define ACTION_MODE_ADD 1
+#define ACTION_MODE_REM 2
+#define ACTION_MODE_OCT 3
+
+struct mode_data {
+       struct mode_data *next;
+       int operation;
+       int mode;
+       unsigned int mask;
+       char X;
+};
+
+
+/*
+ * Empty action specific definitions
+ */
+#define EMPTY_ALL 0
+#define EMPTY_SOURCE 1
+#define EMPTY_EXCLUDED 2
+
+struct empty_data {
+       int val;
+};
+
+
+/*
+ * Move action specific definitions
+ */
+#define ACTION_MOVE_RENAME 1
+#define ACTION_MOVE_MOVE 2
+
+struct move_ent {
+       int ops;
+       struct dir_ent *dir_ent;
+       char *name;
+       struct dir_info *dest;
+       struct move_ent *next;
+};
+
+
+/*
+ * Perm test function specific definitions
+ */
+#define PERM_ALL 1
+#define PERM_ANY 2
+#define PERM_EXACT 3
+
+struct perm_data {
+       int op;
+       int mode;
+};
+
+
+/*
+ * External function definitions
+ */
+extern int parse_action(char *, int verbose);
+extern void dump_actions();
+extern void *eval_frag_actions(struct dir_info *, struct dir_ent *);
+extern void *get_frag_action(void *);
+extern int eval_exclude_actions(char *, char *, char *, struct stat *, int,
+                                                       struct dir_ent *);
+extern void eval_actions(struct dir_info *, struct dir_ent *);
+extern int eval_empty_actions(struct dir_info *, struct dir_ent *dir_ent);
+extern void eval_move_actions(struct dir_info *, struct dir_ent *);
+extern int eval_prune_actions(struct dir_info *, struct dir_ent *);
+extern void do_move_actions();
+extern int read_bytes(int, void *, int);
+extern int actions();
+extern int move_actions();
+extern int empty_actions();
+extern int read_action_file(char *, int);
+extern int exclude_actions();
+extern int prune_actions();
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/build.sh b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/build.sh
new file mode 100644 (file)
index 0000000..b3bc144
--- /dev/null
@@ -0,0 +1,35 @@
+#!/bin/bash
+
+export LZMA_LIBDIR=$PWD/../../LIB/LZMA
+export LZ4_LIBDIR=$PWD/../../LIB/LZ4
+export ZSTD_LIBDIR=$PWD/../../LIB/ZSTD
+export LZO_LIBDIR=$PWD/../../LIB/LZO
+
+if [ -e /lib64/libz.a ]; then
+    export VTZLIB=/lib64/libz.a
+elif [ -e /lib/libz.a ]; then
+    export VTZLIB=/lib/libz.a
+elif [ -e /usr/lib/libz.a ]; then
+    export VTZLIB=/usr/lib/libz.a
+fi
+
+rm -f unsquashfs
+make clean
+make -e unsquashfs
+
+if [ -e unsquashfs ]; then
+    strip --strip-all unsquashfs
+    echo -e "\n========== SUCCESS ============\n"
+else
+    echo -e "\n========== FAILED ============\n"
+fi
+
+if uname -a | egrep -q 'x86_64|amd64'; then
+    name=unsquashfs_64
+else
+    name=unsquashfs_32
+fi
+
+rm -f ../../$name
+cp -a unsquashfs ../../$name
+
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/caches-queues-lists.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/caches-queues-lists.c
new file mode 100644 (file)
index 0000000..e86a517
--- /dev/null
@@ -0,0 +1,642 @@
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2013, 2014, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * caches-queues-lists.c
+ */
+
+#include <pthread.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#include "error.h"
+#include "caches-queues-lists.h"
+
+extern int add_overflow(int, int);
+extern int multiply_overflow(int, int);
+
+#define TRUE 1
+#define FALSE 0
+
+struct queue *queue_init(int size)
+{
+       struct queue *queue = malloc(sizeof(struct queue));
+
+       if(queue == NULL)
+               MEM_ERROR();
+
+       if(add_overflow(size, 1) ||
+                               multiply_overflow(size + 1, sizeof(void *)))
+               BAD_ERROR("Size too large in queue_init\n");
+
+       queue->data = malloc(sizeof(void *) * (size + 1));
+       if(queue->data == NULL)
+               MEM_ERROR();
+
+       queue->size = size + 1;
+       queue->readp = queue->writep = 0;
+       pthread_mutex_init(&queue->mutex, NULL);
+       pthread_cond_init(&queue->empty, NULL);
+       pthread_cond_init(&queue->full, NULL);
+
+       return queue;
+}
+
+
+void queue_put(struct queue *queue, void *data)
+{
+       int nextp;
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
+       pthread_mutex_lock(&queue->mutex);
+
+       while((nextp = (queue->writep + 1) % queue->size) == queue->readp)
+               pthread_cond_wait(&queue->full, &queue->mutex);
+
+       queue->data[queue->writep] = data;
+       queue->writep = nextp;
+       pthread_cond_signal(&queue->empty);
+       pthread_cleanup_pop(1);
+}
+
+
+void *queue_get(struct queue *queue)
+{
+       void *data;
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
+       pthread_mutex_lock(&queue->mutex);
+
+       while(queue->readp == queue->writep)
+               pthread_cond_wait(&queue->empty, &queue->mutex);
+
+       data = queue->data[queue->readp];
+       queue->readp = (queue->readp + 1) % queue->size;
+       pthread_cond_signal(&queue->full);
+       pthread_cleanup_pop(1);
+
+       return data;
+}
+
+
+int queue_empty(struct queue *queue)
+{
+       int empty;
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
+       pthread_mutex_lock(&queue->mutex);
+
+       empty = queue->readp == queue->writep;
+
+       pthread_cleanup_pop(1);
+
+       return empty;
+}
+
+
+void queue_flush(struct queue *queue)
+{
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
+       pthread_mutex_lock(&queue->mutex);
+
+       queue->readp = queue->writep;
+
+       pthread_cleanup_pop(1);
+}
+
+
+void dump_queue(struct queue *queue)
+{
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
+       pthread_mutex_lock(&queue->mutex);
+
+       printf("\tMax size %d, size %d%s\n", queue->size - 1,  
+               queue->readp <= queue->writep ? queue->writep - queue->readp :
+                       queue->size - queue->readp + queue->writep,
+               queue->readp == queue->writep ? " (EMPTY)" :
+                       ((queue->writep + 1) % queue->size) == queue->readp ?
+                       " (FULL)" : "");
+
+       pthread_cleanup_pop(1);
+}
+
+
+/* define seq queue hash tables */
+#define CALCULATE_SEQ_HASH(N) CALCULATE_HASH(N)
+
+/* Called with the seq queue mutex held */
+INSERT_HASH_TABLE(seq, struct seq_queue, CALCULATE_SEQ_HASH, sequence, seq)
+
+/* Called with the cache mutex held */
+REMOVE_HASH_TABLE(seq, struct seq_queue, CALCULATE_SEQ_HASH, sequence, seq);
+
+
+struct seq_queue *seq_queue_init()
+{
+       struct seq_queue *queue = malloc(sizeof(struct seq_queue));
+       if(queue == NULL)
+               MEM_ERROR();
+
+       memset(queue, 0, sizeof(struct seq_queue));
+
+       pthread_mutex_init(&queue->mutex, NULL);
+       pthread_cond_init(&queue->wait, NULL);
+
+       return queue;
+}
+
+
+void seq_queue_put(struct seq_queue *queue, struct file_buffer *entry)
+{
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
+       pthread_mutex_lock(&queue->mutex);
+
+       insert_seq_hash_table(queue, entry);
+
+       if(entry->fragment)
+               queue->fragment_count ++;
+       else
+               queue->block_count ++;
+
+       if(entry->sequence == queue->sequence)
+               pthread_cond_signal(&queue->wait);
+
+       pthread_cleanup_pop(1);
+}
+
+
+struct file_buffer *seq_queue_get(struct seq_queue *queue)
+{
+       /*
+        * Return next buffer from queue in sequence order (queue->sequence).  If
+        * found return it, otherwise wait for it to arrive.
+        */
+       int hash = CALCULATE_SEQ_HASH(queue->sequence);
+       struct file_buffer *entry;
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
+       pthread_mutex_lock(&queue->mutex);
+
+       while(1) {
+               for(entry = queue->hash_table[hash]; entry;
+                                               entry = entry->seq_next)
+                       if(entry->sequence == queue->sequence)
+                               break;
+
+               if(entry) {
+                       /*
+                        * found the buffer in the queue, decrement the
+                        * appropriate count, and remove from hash list
+                        */
+                       if(entry->fragment)
+                               queue->fragment_count --;
+                       else
+                               queue->block_count --;
+
+                       remove_seq_hash_table(queue, entry);
+
+                       queue->sequence ++;
+
+                       break;
+               }
+
+               /* entry not found, wait for it to arrive */    
+               pthread_cond_wait(&queue->wait, &queue->mutex);
+       }
+
+       pthread_cleanup_pop(1);
+
+       return entry;
+}
+
+
+void seq_queue_flush(struct seq_queue *queue)
+{
+       int i;
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
+       pthread_mutex_lock(&queue->mutex);
+
+       for(i = 0; i < HASH_SIZE; i++)
+               queue->hash_table[i] = NULL;
+
+       queue->fragment_count = queue->block_count = 0;
+
+       pthread_cleanup_pop(1);
+}
+
+
+void dump_seq_queue(struct seq_queue *queue, int fragment_queue)
+{
+       int size;
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &queue->mutex);
+       pthread_mutex_lock(&queue->mutex);
+
+       size = fragment_queue ? queue->fragment_count : queue->block_count;
+
+       printf("\tMax size unlimited, size %d%s\n", size,
+                                               size == 0 ? " (EMPTY)" : "");
+
+       pthread_cleanup_pop(1);
+}
+
+
+/* define cache hash tables */
+#define CALCULATE_CACHE_HASH(N) CALCULATE_HASH(llabs(N))
+
+/* Called with the cache mutex held */
+INSERT_HASH_TABLE(cache, struct cache, CALCULATE_CACHE_HASH, index, hash)
+
+/* Called with the cache mutex held */
+REMOVE_HASH_TABLE(cache, struct cache, CALCULATE_CACHE_HASH, index, hash);
+
+/* define cache free list */
+
+/* Called with the cache mutex held */
+INSERT_LIST(free, struct file_buffer)
+
+/* Called with the cache mutex held */
+REMOVE_LIST(free, struct file_buffer)
+
+
+struct cache *cache_init(int buffer_size, int max_buffers, int noshrink_lookup,
+       int first_freelist)
+{
+       struct cache *cache = malloc(sizeof(struct cache));
+
+       if(cache == NULL)
+               MEM_ERROR();
+
+       cache->max_buffers = max_buffers;
+       cache->buffer_size = buffer_size;
+       cache->count = 0;
+       cache->used = 0;
+       cache->free_list = NULL;
+
+       /*
+        * The cache will grow up to max_buffers in size in response to
+        * an increase in readhead/number of buffers in flight.  But
+        * once the outstanding buffers gets returned, we can either elect
+        * to shrink the cache, or to put the freed blocks onto a free list.
+        *
+        * For the caches where we want to do lookup (fragment/writer),
+        * a don't shrink policy is best, for the reader cache it
+        * makes no sense to keep buffers around longer than necessary as
+        * we don't do any lookup on those blocks.
+        */
+       cache->noshrink_lookup = noshrink_lookup;
+
+       /*
+        * The default use freelist before growing cache policy behaves
+        * poorly with appending - with many duplicates the caches
+        * do not grow due to the fact that large queues of outstanding
+        * fragments/writer blocks do not occur, leading to small caches
+        * and un-uncessary performance loss to frequent cache
+        * replacement in the small caches.  Therefore with appending
+        * change the policy to grow the caches before reusing blocks
+        * from the freelist
+        */
+       cache->first_freelist = first_freelist;
+
+       memset(cache->hash_table, 0, sizeof(struct file_buffer *) * 65536);
+       pthread_mutex_init(&cache->mutex, NULL);
+       pthread_cond_init(&cache->wait_for_free, NULL);
+       pthread_cond_init(&cache->wait_for_unlock, NULL);
+
+       return cache;
+}
+
+
+struct file_buffer *cache_lookup(struct cache *cache, long long index)
+{
+       /* Lookup block in the cache, if found return with usage count
+        * incremented, if not found return NULL */
+       int hash = CALCULATE_CACHE_HASH(index);
+       struct file_buffer *entry;
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
+       pthread_mutex_lock(&cache->mutex);
+
+       for(entry = cache->hash_table[hash]; entry; entry = entry->hash_next)
+               if(entry->index == index)
+                       break;
+
+       if(entry) {
+               /* found the block in the cache, increment used count and
+                * if necessary remove from free list so it won't disappear
+                */
+               if(entry->used == 0) {
+                       remove_free_list(&cache->free_list, entry);
+                       cache->used ++;
+               }
+               entry->used ++;
+       }
+
+       pthread_cleanup_pop(1);
+
+       return entry;
+}
+
+
+static struct file_buffer *cache_freelist(struct cache *cache)
+{
+       struct file_buffer *entry = cache->free_list;
+
+       remove_free_list(&cache->free_list, entry);
+
+       /* a block on the free_list is hashed */
+       remove_cache_hash_table(cache, entry);
+
+       cache->used ++;
+       return entry;
+}
+
+
+static struct file_buffer *cache_alloc(struct cache *cache)
+{
+       struct file_buffer *entry = malloc(sizeof(struct file_buffer) +
+                                                       cache->buffer_size);
+       if(entry == NULL)
+                       MEM_ERROR();
+
+       entry->cache = cache;
+       entry->free_prev = entry->free_next = NULL;
+       cache->count ++;
+       return entry;
+}
+
+
+static struct file_buffer *_cache_get(struct cache *cache, long long index,
+       int hash)
+{
+       /* Get a free block out of the cache indexed on index. */
+       struct file_buffer *entry = NULL;
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
+       pthread_mutex_lock(&cache->mutex);
+
+       while(1) {
+               if(cache->noshrink_lookup) {    
+                       /* first try to get a block from the free list */
+                       if(cache->first_freelist && cache->free_list)
+                               entry = cache_freelist(cache);
+                       else if(cache->count < cache->max_buffers) {
+                               entry = cache_alloc(cache);
+                               cache->used ++;
+                       } else if(!cache->first_freelist && cache->free_list)
+                               entry = cache_freelist(cache);
+               } else { /* shrinking non-lookup cache */
+                       if(cache->count < cache->max_buffers) {
+                               entry = cache_alloc(cache);
+                               if(cache->count > cache->max_count)
+                                       cache->max_count = cache->count;
+                       }
+               }
+
+               if(entry)
+                       break;
+
+               /* wait for a block */
+               pthread_cond_wait(&cache->wait_for_free, &cache->mutex);
+       }
+
+       /* initialise block and if hash is set insert into the hash table */
+       entry->used = 1;
+       entry->locked = FALSE;
+       entry->wait_on_unlock = FALSE;
+       entry->error = FALSE;
+       if(hash) {
+               entry->index = index;
+               insert_cache_hash_table(cache, entry);
+       }
+
+       pthread_cleanup_pop(1);
+
+       return entry;
+}
+
+
+struct file_buffer *cache_get(struct cache *cache, long long index)
+{
+       return _cache_get(cache, index, 1);
+}
+
+
+struct file_buffer *cache_get_nohash(struct cache *cache)
+{
+       return _cache_get(cache, 0, 0);
+}
+
+
+void cache_hash(struct file_buffer *entry, long long index)
+{
+       struct cache *cache = entry->cache;
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
+       pthread_mutex_lock(&cache->mutex);
+
+       entry->index = index;
+       insert_cache_hash_table(cache, entry);
+
+       pthread_cleanup_pop(1);
+}
+
+
+void cache_block_put(struct file_buffer *entry)
+{
+       struct cache *cache;
+
+       /*
+        * Finished with this cache entry, once the usage count reaches zero it
+        * can be reused.
+        *
+        * If noshrink_lookup is set, put the block onto the free list.
+        * As blocks remain accessible via the hash table they can be found
+        * getting a new lease of life before they are reused.
+        *
+        * if noshrink_lookup is not set then shrink the cache.
+        */
+
+       if(entry == NULL)
+               return;
+
+       cache = entry->cache;
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
+       pthread_mutex_lock(&cache->mutex);
+
+       entry->used --;
+       if(entry->used == 0) {
+               if(cache->noshrink_lookup) {
+                       insert_free_list(&cache->free_list, entry);
+                       cache->used --;
+               } else {
+                       free(entry);
+                       cache->count --;
+               }
+
+               /* One or more threads may be waiting on this block */
+               pthread_cond_signal(&cache->wait_for_free);
+       }
+
+       pthread_cleanup_pop(1);
+}
+
+
+void dump_cache(struct cache *cache)
+{
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
+       pthread_mutex_lock(&cache->mutex);
+
+       if(cache->noshrink_lookup)
+               printf("\tMax buffers %d, Current size %d, Used %d,  %s\n",
+                       cache->max_buffers, cache->count, cache->used,
+                       cache->free_list ?  "Free buffers" : "No free buffers");
+       else
+               printf("\tMax buffers %d, Current size %d, Maximum historical "
+                       "size %d\n", cache->max_buffers, cache->count,
+                       cache->max_count);
+
+       pthread_cleanup_pop(1);
+}
+
+
+struct file_buffer *cache_get_nowait(struct cache *cache, long long index)
+{
+       struct file_buffer *entry = NULL;
+       /*
+        * block doesn't exist, create it, but return it with the
+        * locked flag set, so nothing tries to use it while it doesn't
+        * contain data.
+        *
+        * If there's no space in the cache then return NULL.
+        */
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
+       pthread_mutex_lock(&cache->mutex);
+
+       /* first try to get a block from the free list */
+       if(cache->first_freelist && cache->free_list)
+               entry = cache_freelist(cache);
+       else if(cache->count < cache->max_buffers) {
+               entry = cache_alloc(cache);
+               cache->used ++;
+       } else if(!cache->first_freelist && cache->free_list)
+               entry = cache_freelist(cache);
+
+       if(entry) {
+               /* initialise block and insert into the hash table */
+               entry->used = 1;
+               entry->locked = TRUE;
+               entry->wait_on_unlock = FALSE;
+               entry->error = FALSE;
+               entry->index = index;
+               insert_cache_hash_table(cache, entry);
+       }
+
+       pthread_cleanup_pop(1);
+
+       return entry;
+}
+
+
+struct file_buffer *cache_lookup_nowait(struct cache *cache, long long index,
+       char *locked)
+{
+       /*
+        * Lookup block in the cache, if found return it with the locked flag
+        * indicating whether it is currently locked.  In both cases increment
+        * the used count.
+        *
+        * If it doesn't exist in the cache return NULL;
+        */
+       int hash = CALCULATE_CACHE_HASH(index);
+       struct file_buffer *entry;
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
+       pthread_mutex_lock(&cache->mutex);
+
+       /* first check if the entry already exists */
+       for(entry = cache->hash_table[hash]; entry; entry = entry->hash_next)
+               if(entry->index == index)
+                       break;
+
+       if(entry) {
+               if(entry->used == 0) {
+                       remove_free_list(&cache->free_list, entry);
+                       cache->used ++;
+               }
+               entry->used ++;
+               *locked = entry->locked;
+       }
+
+       pthread_cleanup_pop(1);
+
+       return entry;
+}
+
+
+void cache_wait_unlock(struct file_buffer *buffer)
+{
+       struct cache *cache = buffer->cache;
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
+       pthread_mutex_lock(&cache->mutex);
+
+       while(buffer->locked) {
+               /*
+                * another thread is filling this in, wait until it
+                * becomes unlocked.  Used has been incremented to ensure it
+                * doesn't get reused.  By definition a block can't be
+                * locked and unused, and so we don't need to worry
+                * about it being on the freelist now, but, it may
+                * become unused when unlocked unless used is
+                * incremented
+                */
+               buffer->wait_on_unlock = TRUE;
+               pthread_cond_wait(&cache->wait_for_unlock, &cache->mutex);
+       }
+
+       pthread_cleanup_pop(1);
+}
+
+
+void cache_unlock(struct file_buffer *entry)
+{
+       struct cache *cache = entry->cache;
+
+       /*
+        * Unlock this locked cache entry.  If anything is waiting for this
+        * to become unlocked, wake it up.
+        */
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &cache->mutex);
+       pthread_mutex_lock(&cache->mutex);
+
+       entry->locked = FALSE;
+
+       if(entry->wait_on_unlock) {
+               entry->wait_on_unlock = FALSE;
+               pthread_cond_broadcast(&cache->wait_for_unlock);
+       }
+
+       pthread_cleanup_pop(1);
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/caches-queues-lists.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/caches-queues-lists.h
new file mode 100644 (file)
index 0000000..0e17e91
--- /dev/null
@@ -0,0 +1,199 @@
+#ifndef CACHES_QUEUES_LISTS_H
+#define CACHES_QUEUES_LISTS_H
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2013, 2014, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * caches-queues-lists.h
+ */
+
+#define INSERT_LIST(NAME, TYPE) \
+void insert_##NAME##_list(TYPE **list, TYPE *entry) { \
+       if(*list) { \
+               entry->NAME##_next = *list; \
+               entry->NAME##_prev = (*list)->NAME##_prev; \
+               (*list)->NAME##_prev->NAME##_next = entry; \
+               (*list)->NAME##_prev = entry; \
+       } else { \
+               *list = entry; \
+               entry->NAME##_prev = entry->NAME##_next = entry; \
+       } \
+}
+
+
+#define REMOVE_LIST(NAME, TYPE) \
+void remove_##NAME##_list(TYPE **list, TYPE *entry) { \
+       if(entry->NAME##_prev == entry && entry->NAME##_next == entry) { \
+               /* only this entry in the list */ \
+               *list = NULL; \
+       } else if(entry->NAME##_prev != NULL && entry->NAME##_next != NULL) { \
+               /* more than one entry in the list */ \
+               entry->NAME##_next->NAME##_prev = entry->NAME##_prev; \
+               entry->NAME##_prev->NAME##_next = entry->NAME##_next; \
+               if(*list == entry) \
+                       *list = entry->NAME##_next; \
+       } \
+       entry->NAME##_prev = entry->NAME##_next = NULL; \
+}
+
+
+#define INSERT_HASH_TABLE(NAME, TYPE, HASH_FUNCTION, FIELD, LINK) \
+void insert_##NAME##_hash_table(TYPE *container, struct file_buffer *entry) \
+{ \
+       int hash = HASH_FUNCTION(entry->FIELD); \
+\
+       entry->LINK##_next = container->hash_table[hash]; \
+       container->hash_table[hash] = entry; \
+       entry->LINK##_prev = NULL; \
+       if(entry->LINK##_next) \
+               entry->LINK##_next->LINK##_prev = entry; \
+}
+
+
+#define REMOVE_HASH_TABLE(NAME, TYPE, HASH_FUNCTION, FIELD, LINK) \
+void remove_##NAME##_hash_table(TYPE *container, struct file_buffer *entry) \
+{ \
+       if(entry->LINK##_prev) \
+               entry->LINK##_prev->LINK##_next = entry->LINK##_next; \
+       else \
+               container->hash_table[HASH_FUNCTION(entry->FIELD)] = \
+                       entry->LINK##_next; \
+       if(entry->LINK##_next) \
+               entry->LINK##_next->LINK##_prev = entry->LINK##_prev; \
+\
+       entry->LINK##_prev = entry->LINK##_next = NULL; \
+}
+
+#define HASH_SIZE 65536
+#define CALCULATE_HASH(n) ((n) & 0xffff)
+
+
+/* struct describing a cache entry passed between threads */
+struct file_buffer {
+       long long index;
+       long long sequence;
+       long long file_size;
+       union {
+               long long block;
+               unsigned short checksum;
+       };
+       struct cache *cache;
+       union {
+               struct file_info *dupl_start;
+               struct file_buffer *hash_next;
+       };
+       union {
+               int duplicate;
+               struct file_buffer *hash_prev;
+       };
+       union {
+               struct {
+                       struct file_buffer *free_next;
+                       struct file_buffer *free_prev;
+               };
+               struct {
+                       struct file_buffer *seq_next;
+                       struct file_buffer *seq_prev;
+               };
+       };
+       int size;
+       int c_byte;
+       char used;
+       char fragment;
+       char error;
+       char locked;
+       char wait_on_unlock;
+       char noD;
+       char data[0] __attribute__((aligned));
+};
+
+
+/* struct describing queues used to pass data between threads */
+struct queue {
+       int                     size;
+       int                     readp;
+       int                     writep;
+       pthread_mutex_t         mutex;
+       pthread_cond_t          empty;
+       pthread_cond_t          full;
+       void                    **data;
+};
+
+
+/*
+ * struct describing seq_queues used to pass data between the read
+ * thread and the deflate and main threads
+ */
+struct seq_queue {
+       int                     fragment_count;
+       int                     block_count;
+       long long               sequence;
+       struct file_buffer      *hash_table[HASH_SIZE];
+       pthread_mutex_t         mutex;
+       pthread_cond_t          wait;
+};
+
+
+/* Cache status struct.  Caches are used to keep
+  track of memory buffers passed between different threads */
+struct cache {
+       int     max_buffers;
+       int     count;
+       int     buffer_size;
+       int     noshrink_lookup;
+       int     first_freelist;
+       union {
+               int     used;
+               int     max_count;
+       };
+       pthread_mutex_t mutex;
+       pthread_cond_t wait_for_free;
+       pthread_cond_t wait_for_unlock;
+       struct file_buffer *free_list;
+       struct file_buffer *hash_table[HASH_SIZE];
+};
+
+
+extern struct queue *queue_init(int);
+extern void queue_put(struct queue *, void *);
+extern void *queue_get(struct queue *);
+extern int queue_empty(struct queue *);
+extern void queue_flush(struct queue *);
+extern void dump_queue(struct queue *);
+extern struct seq_queue *seq_queue_init();
+extern void seq_queue_put(struct seq_queue *, struct file_buffer *);
+extern void dump_seq_queue(struct seq_queue *, int);
+extern struct file_buffer *seq_queue_get(struct seq_queue *);
+extern void seq_queue_flush(struct seq_queue *);
+extern struct cache *cache_init(int, int, int, int);
+extern struct file_buffer *cache_lookup(struct cache *, long long);
+extern struct file_buffer *cache_get(struct cache *, long long);
+extern struct file_buffer *cache_get_nohash(struct cache *);
+extern void cache_hash(struct file_buffer *, long long);
+extern void cache_block_put(struct file_buffer *);
+extern void dump_cache(struct cache *);
+extern struct file_buffer *cache_get_nowait(struct cache *, long long);
+extern struct file_buffer *cache_lookup_nowait(struct cache *, long long,
+       char *);
+extern void cache_wait_unlock(struct file_buffer *);
+extern void cache_unlock(struct file_buffer *);
+
+extern int first_freelist;
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/compressor.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/compressor.c
new file mode 100644 (file)
index 0000000..02b5e90
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ *
+ * Copyright (c) 2009, 2010, 2011
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * compressor.c
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include "compressor.h"
+#include "squashfs_fs.h"
+
+#ifndef GZIP_SUPPORT
+static struct compressor gzip_comp_ops =  {
+       ZLIB_COMPRESSION, "gzip"
+};
+#else
+extern struct compressor gzip_comp_ops;
+#endif
+
+#ifndef LZMA_SUPPORT
+static struct compressor lzma_comp_ops = {
+       LZMA_COMPRESSION, "lzma"
+};
+#else
+extern struct compressor lzma_comp_ops;
+#endif
+
+#ifndef LZO_SUPPORT
+static struct compressor lzo_comp_ops = {
+       LZO_COMPRESSION, "lzo"
+};
+#else
+extern struct compressor lzo_comp_ops;
+#endif
+
+#ifndef LZ4_SUPPORT
+static struct compressor lz4_comp_ops = {
+       LZ4_COMPRESSION, "lz4"
+};
+#else
+extern struct compressor lz4_comp_ops;
+#endif
+
+#ifndef XZ_SUPPORT
+static struct compressor xz_comp_ops = {
+       XZ_COMPRESSION, "xz"
+};
+#else
+extern struct compressor xz_comp_ops;
+#endif
+
+#ifndef ZSTD_SUPPORT
+static struct compressor zstd_comp_ops = {
+       ZSTD_COMPRESSION, "zstd"
+};
+#else
+extern struct compressor zstd_comp_ops;
+#endif
+
+static struct compressor unknown_comp_ops = {
+       0, "unknown"
+};
+
+
+struct compressor *compressor[] = {
+       &gzip_comp_ops,
+       &lzma_comp_ops,
+       &lzo_comp_ops,
+       &lz4_comp_ops,
+       &xz_comp_ops,
+       &zstd_comp_ops,
+       &unknown_comp_ops
+};
+
+
+struct compressor *lookup_compressor(char *name)
+{
+       int i;
+
+       for(i = 0; compressor[i]->id; i++)
+               if(strcmp(compressor[i]->name, name) == 0)
+                       break;
+
+       return compressor[i];
+}
+
+
+struct compressor *lookup_compressor_id(int id)
+{
+       int i;
+
+       for(i = 0; compressor[i]->id; i++)
+               if(id == compressor[i]->id)
+                       break;
+
+       return compressor[i];
+}
+
+
+void display_compressors(char *indent, char *def_comp)
+{
+       int i;
+
+       for(i = 0; compressor[i]->id; i++)
+               if(compressor[i]->supported)
+                       fprintf(stderr, "%s\t%s%s\n", indent,
+                               compressor[i]->name,
+                               strcmp(compressor[i]->name, def_comp) == 0 ?
+                               " (default)" : "");
+}
+
+
+void display_compressor_usage(char *def_comp)
+{
+       int i;
+
+       for(i = 0; compressor[i]->id; i++)
+               if(compressor[i]->supported) {
+                       char *str = strcmp(compressor[i]->name, def_comp) == 0 ?
+                               " (default)" : "";
+                       if(compressor[i]->usage) {
+                               fprintf(stderr, "\t%s%s\n",
+                                       compressor[i]->name, str);
+                               compressor[i]->usage();
+                       } else
+                               fprintf(stderr, "\t%s (no options)%s\n",
+                                       compressor[i]->name, str);
+               }
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/compressor.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/compressor.h
new file mode 100644 (file)
index 0000000..4679d91
--- /dev/null
@@ -0,0 +1,124 @@
+#ifndef COMPRESSOR_H
+#define COMPRESSOR_H
+/*
+ *
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * compressor.h
+ */
+
+struct compressor {
+       int id;
+       char *name;
+       int supported;
+       int (*init)(void **, int, int);
+       int (*compress)(void *, void *, void *, int, int, int *);
+       int (*uncompress)(void *, void *, int, int, int *);
+       int (*options)(char **, int);
+       int (*options_post)(int);
+       void *(*dump_options)(int, int *);
+       int (*extract_options)(int, void *, int);
+       int (*check_options)(int, void *, int);
+       void (*display_options)(void *, int);
+       void (*usage)();
+};
+
+extern struct compressor *lookup_compressor(char *);
+extern struct compressor *lookup_compressor_id(int);
+extern void display_compressors(char *, char *);
+extern void display_compressor_usage(char *);
+
+static inline int compressor_init(struct compressor *comp, void **stream,
+       int block_size, int datablock)
+{
+       if(comp->init == NULL)
+               return 0;
+       return comp->init(stream, block_size, datablock);
+}
+
+
+static inline int compressor_compress(struct compressor *comp, void *strm,
+       void *dest, void *src, int size, int block_size, int *error)
+{
+       return comp->compress(strm, dest, src, size, block_size, error);
+}
+
+
+static inline int compressor_uncompress(struct compressor *comp, void *dest,
+       void *src, int size, int block_size, int *error)
+{
+       return comp->uncompress(dest, src, size, block_size, error);
+}
+
+
+/*
+ * For the following functions please see the lzo, lz4 or xz
+ * compressors for commented examples of how they are used.
+ */
+static inline int compressor_options(struct compressor *comp, char *argv[],
+       int argc)
+{
+       if(comp->options == NULL)
+               return -1;
+
+       return comp->options(argv, argc);
+}
+
+
+static inline int compressor_options_post(struct compressor *comp, int block_size)
+{
+       if(comp->options_post == NULL)
+               return 0;
+       return comp->options_post(block_size);
+}
+
+
+static inline void *compressor_dump_options(struct compressor *comp,
+       int block_size, int *size)
+{
+       if(comp->dump_options == NULL)
+               return NULL;
+       return comp->dump_options(block_size, size);
+}
+
+
+static inline int compressor_extract_options(struct compressor *comp,
+       int block_size, void *buffer, int size)
+{
+       if(comp->extract_options == NULL)
+               return size ? -1 : 0;
+       return comp->extract_options(block_size, buffer, size);
+}
+
+
+static inline int compressor_check_options(struct compressor *comp,
+       int block_size, void *buffer, int size)
+{
+       if(comp->check_options == NULL)
+               return 0;
+       return comp->check_options(block_size, buffer, size);
+}
+
+
+static inline void compressor_display_options(struct compressor *comp,
+       void *buffer, int size)
+{
+       if(comp->display_options != NULL)
+               comp->display_options(buffer, size);
+}
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/error.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/error.h
new file mode 100644 (file)
index 0000000..d7cd91a
--- /dev/null
@@ -0,0 +1,106 @@
+#ifndef ERROR_H
+#define ERROR_H
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2012, 2013, 2014, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * error.h
+ */
+
+extern int exit_on_error;
+
+extern void prep_exit();
+extern void progressbar_error(char *fmt, ...);
+extern void progressbar_info(char *fmt, ...);
+
+#ifdef SQUASHFS_TRACE
+#define TRACE(s, args...) \
+               do { \
+                       progressbar_info("squashfs: "s, ## args);\
+               } while(0)
+#else
+#define TRACE(s, args...)
+#endif
+
+#define INFO(s, args...) \
+               do {\
+                        if(!silent)\
+                               progressbar_info(s, ## args);\
+               } while(0)
+
+#define ERROR(s, args...) \
+               do {\
+                       progressbar_error(s, ## args); \
+               } while(0)
+
+#define ERROR_START(s, args...) \
+               do { \
+                       disable_progress_bar(); \
+                       fprintf(stderr, s, ## args); \
+               } while(0)
+
+#define ERROR_EXIT(s, args...) \
+               do {\
+                       if (exit_on_error) { \
+                               fprintf(stderr, "\n"); \
+                               EXIT_MKSQUASHFS(); \
+                       } else { \
+                               fprintf(stderr, s, ## args); \
+                               enable_progress_bar(); \
+                       } \
+               } while(0)
+
+#define EXIT_MKSQUASHFS() \
+               do {\
+                       prep_exit();\
+                       exit(1);\
+               } while(0)
+
+#define BAD_ERROR(s, args...) \
+               do {\
+                       progressbar_error("FATAL ERROR:" s, ##args); \
+                       EXIT_MKSQUASHFS();\
+               } while(0)
+
+#define EXIT_UNSQUASH(s, args...) BAD_ERROR(s, ##args)
+
+#define EXIT_UNSQUASH_IGNORE(s, args...) \
+       do {\
+               if(ignore_errors) \
+                       ERROR(s, ##args); \
+               else \
+                       BAD_ERROR(s, ##args); \
+       } while(0)
+
+#define EXIT_UNSQUASH_STRICT(s, args...) \
+       do {\
+               if(!strict_errors) \
+                       ERROR(s, ##args); \
+               else \
+                       BAD_ERROR(s, ##args); \
+       } while(0)
+
+#define MEM_ERROR() \
+       do {\
+               progressbar_error("FATAL ERROR: Out of memory (%s)\n", \
+                                                               __func__); \
+               EXIT_MKSQUASHFS();\
+       } while(0)
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/fnmatch_compat.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/fnmatch_compat.h
new file mode 100644 (file)
index 0000000..7b4afd8
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef FNMATCH_COMPAT
+#define FNMATCH_COMPAT
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2015
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * fnmatch_compat.h
+ */
+
+#include <fnmatch.h>
+
+#ifndef FNM_EXTMATCH
+#define FNM_EXTMATCH   0
+#endif
+
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/gzip_wrapper.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/gzip_wrapper.c
new file mode 100644 (file)
index 0000000..ac32bb9
--- /dev/null
@@ -0,0 +1,500 @@
+/*
+ * Copyright (c) 2009, 2010, 2013, 2014
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * gzip_wrapper.c
+ *
+ * Support for ZLIB compression http://www.zlib.net
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <zlib.h>
+
+#include "squashfs_fs.h"
+#include "gzip_wrapper.h"
+#include "compressor.h"
+
+static struct strategy strategy[] = {
+       { "default", Z_DEFAULT_STRATEGY, 0 },
+       { "filtered", Z_FILTERED, 0 },
+       { "huffman_only", Z_HUFFMAN_ONLY, 0 },
+       { "run_length_encoded", Z_RLE, 0 },
+       { "fixed", Z_FIXED, 0 },
+       { NULL, 0, 0 }
+};
+
+static int strategy_count = 0;
+
+/* default compression level */
+static int compression_level = GZIP_DEFAULT_COMPRESSION_LEVEL;
+
+/* default window size */
+static int window_size = GZIP_DEFAULT_WINDOW_SIZE;
+
+/*
+ * This function is called by the options parsing code in mksquashfs.c
+ * to parse any -X compressor option.
+ *
+ * This function returns:
+ *     >=0 (number of additional args parsed) on success
+ *     -1 if the option was unrecognised, or
+ *     -2 if the option was recognised, but otherwise bad in
+ *        some way (e.g. invalid parameter)
+ *
+ * Note: this function sets internal compressor state, but does not
+ * pass back the results of the parsing other than success/failure.
+ * The gzip_dump_options() function is called later to get the options in
+ * a format suitable for writing to the filesystem.
+ */
+static int gzip_options(char *argv[], int argc)
+{
+       if(strcmp(argv[0], "-Xcompression-level") == 0) {
+               if(argc < 2) {
+                       fprintf(stderr, "gzip: -Xcompression-level missing "
+                               "compression level\n");
+                       fprintf(stderr, "gzip: -Xcompression-level it "
+                               "should be 1 >= n <= 9\n");
+                       goto failed;
+               }
+
+               compression_level = atoi(argv[1]);
+               if(compression_level < 1 || compression_level > 9) {
+                       fprintf(stderr, "gzip: -Xcompression-level invalid, it "
+                               "should be 1 >= n <= 9\n");
+                       goto failed;
+               }
+
+               return 1;
+       } else if(strcmp(argv[0], "-Xwindow-size") == 0) {
+               if(argc < 2) {
+                       fprintf(stderr, "gzip: -Xwindow-size missing window "
+                               "       size\n");
+                       fprintf(stderr, "gzip: -Xwindow-size <window-size>\n");
+                       goto failed;
+               }
+
+               window_size = atoi(argv[1]);
+               if(window_size < 8 || window_size > 15) {
+                       fprintf(stderr, "gzip: -Xwindow-size invalid, it "
+                               "should be 8 >= n <= 15\n");
+                       goto failed;
+               }
+
+               return 1;
+       } else if(strcmp(argv[0], "-Xstrategy") == 0) {
+               char *name;
+               int i;
+
+               if(argc < 2) {
+                       fprintf(stderr, "gzip: -Xstrategy missing "
+                                                       "strategies\n");
+                       goto failed;
+               }
+
+               name = argv[1];
+               while(name[0] != '\0') {
+                       for(i = 0; strategy[i].name; i++) {
+                               int n = strlen(strategy[i].name);
+                               if((strncmp(name, strategy[i].name, n) == 0) &&
+                                               (name[n] == '\0' ||
+                                                name[n] == ',')) {
+                                       if(strategy[i].selected == 0) {
+                                               strategy[i].selected = 1;
+                                               strategy_count++;
+                                       }
+                                       name += name[n] == ',' ? n + 1 : n;
+                                       break;
+                               }
+                       }
+                       if(strategy[i].name == NULL) {
+                               fprintf(stderr, "gzip: -Xstrategy unrecognised "
+                                       "strategy\n");
+                               goto failed;
+                       }
+               }
+       
+               return 1;
+       }
+
+       return -1;
+
+failed:
+       return -2;
+}
+
+
+/*
+ * This function is called after all options have been parsed.
+ * It is used to do post-processing on the compressor options using
+ * values that were not expected to be known at option parse time.
+ *
+ * This function returns 0 on successful post processing, or
+ *                     -1 on error
+ */
+static int gzip_options_post(int block_size)
+{
+       if(strategy_count == 1 && strategy[0].selected) {
+               strategy_count = 0;
+               strategy[0].selected = 0;
+       }
+
+       return 0;
+}
+
+
+/*
+ * This function is called by mksquashfs to dump the parsed
+ * compressor options in a format suitable for writing to the
+ * compressor options field in the filesystem (stored immediately
+ * after the superblock).
+ *
+ * This function returns a pointer to the compression options structure
+ * to be stored (and the size), or NULL if there are no compression
+ * options
+ *
+ */
+static void *gzip_dump_options(int block_size, int *size)
+{
+       static struct gzip_comp_opts comp_opts;
+       int i, strategies = 0;
+
+       /*
+        * If default compression options of:
+        * compression-level: 8 and
+        * window-size: 15 and
+        * strategy_count == 0 then
+        * don't store a compression options structure (this is compatible
+        * with the legacy implementation of GZIP for Squashfs)
+        */
+       if(compression_level == GZIP_DEFAULT_COMPRESSION_LEVEL &&
+                               window_size == GZIP_DEFAULT_WINDOW_SIZE &&
+                               strategy_count == 0)
+               return NULL;
+
+       for(i = 0; strategy[i].name; i++)
+               strategies |= strategy[i].selected << i;
+
+       comp_opts.compression_level = compression_level;
+       comp_opts.window_size = window_size;
+       comp_opts.strategy = strategies;
+
+       SQUASHFS_INSWAP_COMP_OPTS(&comp_opts);
+
+       *size = sizeof(comp_opts);
+       return &comp_opts;
+}
+
+
+/*
+ * This function is a helper specifically for the append mode of
+ * mksquashfs.  Its purpose is to set the internal compressor state
+ * to the stored compressor options in the passed compressor options
+ * structure.
+ *
+ * In effect this function sets up the compressor options
+ * to the same state they were when the filesystem was originally
+ * generated, this is to ensure on appending, the compressor uses
+ * the same compression options that were used to generate the
+ * original filesystem.
+ *
+ * Note, even if there are no compressor options, this function is still
+ * called with an empty compressor structure (size == 0), to explicitly
+ * set the default options, this is to ensure any user supplied
+ * -X options on the appending mksquashfs command line are over-ridden
+ *
+ * This function returns 0 on sucessful extraction of options, and
+ *                     -1 on error
+ */
+static int gzip_extract_options(int block_size, void *buffer, int size)
+{
+       struct gzip_comp_opts *comp_opts = buffer;
+       int i;
+
+       if(size == 0) {
+               /* Set default values */
+               compression_level = GZIP_DEFAULT_COMPRESSION_LEVEL;
+               window_size = GZIP_DEFAULT_WINDOW_SIZE;
+               strategy_count = 0;
+               return 0;
+       }
+
+       /* we expect a comp_opts structure of sufficient size to be present */
+       if(size < sizeof(*comp_opts))
+               goto failed;
+
+       SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
+
+       /* Check comp_opts structure for correctness */
+       if(comp_opts->compression_level < 1 ||
+                       comp_opts->compression_level > 9) {
+               fprintf(stderr, "gzip: bad compression level in "
+                       "compression options structure\n");
+               goto failed;
+       }
+       compression_level = comp_opts->compression_level;
+
+       if(comp_opts->window_size < 8 ||
+                       comp_opts->window_size > 15) {
+               fprintf(stderr, "gzip: bad window size in "
+                       "compression options structure\n");
+               goto failed;
+       }
+       window_size = comp_opts->window_size;
+
+       strategy_count = 0;
+       for(i = 0; strategy[i].name; i++) {
+               if((comp_opts->strategy >> i) & 1) {
+                       strategy[i].selected = 1;
+                       strategy_count ++;
+               } else
+                       strategy[i].selected = 0;
+       }
+       
+       return 0;
+
+failed:
+       fprintf(stderr, "gzip: error reading stored compressor options from "
+               "filesystem!\n");
+
+       return -1;
+}
+
+
+static void gzip_display_options(void *buffer, int size)
+{
+       struct gzip_comp_opts *comp_opts = buffer;
+       int i, printed;
+
+       /* we expect a comp_opts structure of sufficient size to be present */
+       if(size < sizeof(*comp_opts))
+               goto failed;
+
+       SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
+
+       /* Check comp_opts structure for correctness */
+       if(comp_opts->compression_level < 1 ||
+                       comp_opts->compression_level > 9) {
+               fprintf(stderr, "gzip: bad compression level in "
+                       "compression options structure\n");
+               goto failed;
+       }
+       printf("\tcompression-level %d\n", comp_opts->compression_level);
+
+       if(comp_opts->window_size < 8 ||
+                       comp_opts->window_size > 15) {
+               fprintf(stderr, "gzip: bad window size in "
+                       "compression options structure\n");
+               goto failed;
+       }
+       printf("\twindow-size %d\n", comp_opts->window_size);
+
+       for(i = 0, printed = 0; strategy[i].name; i++) {
+               if((comp_opts->strategy >> i) & 1) {
+                       if(printed)
+                               printf(", ");
+                       else
+                               printf("\tStrategies selected: ");
+                       printf("%s", strategy[i].name);
+                       printed = 1;
+               }
+       }
+
+       if(!printed)
+               printf("\tStrategies selected: default\n");
+       else
+               printf("\n");
+
+       return;
+
+failed:
+       fprintf(stderr, "gzip: error reading stored compressor options from "
+               "filesystem!\n");
+}      
+
+
+/*
+ * This function is called by mksquashfs to initialise the
+ * compressor, before compress() is called.
+ *
+ * This function returns 0 on success, and
+ *                     -1 on error
+ */
+static int gzip_init(void **strm, int block_size, int datablock)
+{
+       int i, j, res;
+       struct gzip_stream *stream;
+
+       if(!datablock || !strategy_count) {
+               stream = malloc(sizeof(*stream) + sizeof(struct gzip_strategy));
+               if(stream == NULL)
+                       goto failed;
+
+               stream->strategies = 1;
+               stream->strategy[0].strategy = Z_DEFAULT_STRATEGY;
+       } else {
+               stream = malloc(sizeof(*stream) +
+                       sizeof(struct gzip_strategy) * strategy_count);
+               if(stream == NULL)
+                       goto failed;
+
+               memset(stream->strategy, 0, sizeof(struct gzip_strategy) *
+                       strategy_count);
+
+               stream->strategies = strategy_count;
+
+               for(i = 0, j = 0; strategy[i].name; i++) {
+                       if(!strategy[i].selected)
+                               continue;
+
+                       stream->strategy[j].strategy = strategy[i].strategy;
+                       if(j) {
+                               stream->strategy[j].buffer = malloc(block_size);
+                               if(stream->strategy[j].buffer == NULL)
+                                       goto failed2;
+                       }
+                       j++;
+               }
+       }
+               
+       stream->stream.zalloc = Z_NULL;
+       stream->stream.zfree = Z_NULL;
+       stream->stream.opaque = 0;
+
+       res = deflateInit2(&stream->stream, compression_level, Z_DEFLATED,
+               window_size, 8, stream->strategy[0].strategy);
+       if(res != Z_OK)
+               goto failed2;
+
+       *strm = stream;
+       return 0;
+
+failed2:
+       for(i = 1; i < stream->strategies; i++)
+               free(stream->strategy[i].buffer);
+       free(stream);
+failed:
+       return -1;
+}
+
+
+static int gzip_compress(void *strm, void *d, void *s, int size, int block_size,
+               int *error)
+{
+       int i, res;
+       struct gzip_stream *stream = strm;
+       struct gzip_strategy *selected = NULL;
+
+       stream->strategy[0].buffer = d;
+
+       for(i = 0; i < stream->strategies; i++) {
+               struct gzip_strategy *strategy = &stream->strategy[i];
+
+               res = deflateReset(&stream->stream);
+               if(res != Z_OK)
+                       goto failed;
+
+               stream->stream.next_in = s;
+               stream->stream.avail_in = size;
+               stream->stream.next_out = strategy->buffer;
+               stream->stream.avail_out = block_size;
+
+               if(stream->strategies > 1) {
+                       res = deflateParams(&stream->stream,
+                               compression_level, strategy->strategy);
+                       if(res != Z_OK)
+                               goto failed;
+               }
+
+               res = deflate(&stream->stream, Z_FINISH);
+               strategy->length = stream->stream.total_out;
+               if(res == Z_STREAM_END) {
+                       if(!selected || selected->length > strategy->length)
+                               selected = strategy;
+               } else if(res != Z_OK)
+                       goto failed;
+       }
+
+       if(!selected)
+               /*
+                * Output buffer overflow.  Return out of buffer space
+                */
+               return 0;
+
+       if(selected->buffer != d)
+               memcpy(d, selected->buffer, selected->length);
+
+       return (int) selected->length;
+
+failed:
+       /*
+        * All other errors return failure, with the compressor
+        * specific error code in *error
+        */
+       *error = res;
+       return -1;
+}
+
+
+static int gzip_uncompress(void *d, void *s, int size, int outsize, int *error)
+{
+       int res;
+       unsigned long bytes = outsize;
+
+       res = uncompress(d, &bytes, s, size);
+
+       if(res == Z_OK)
+               return (int) bytes;
+       else {
+               *error = res;
+               return -1;
+       }
+}
+
+
+static void gzip_usage()
+{
+       fprintf(stderr, "\t  -Xcompression-level <compression-level>\n");
+       fprintf(stderr, "\t\t<compression-level> should be 1 .. 9 (default "
+               "%d)\n", GZIP_DEFAULT_COMPRESSION_LEVEL);
+       fprintf(stderr, "\t  -Xwindow-size <window-size>\n");
+       fprintf(stderr, "\t\t<window-size> should be 8 .. 15 (default "
+               "%d)\n", GZIP_DEFAULT_WINDOW_SIZE);
+       fprintf(stderr, "\t  -Xstrategy strategy1,strategy2,...,strategyN\n");
+       fprintf(stderr, "\t\tCompress using strategy1,strategy2,...,strategyN"
+               " in turn\n");
+       fprintf(stderr, "\t\tand choose the best compression.\n");
+       fprintf(stderr, "\t\tAvailable strategies: default, filtered, "
+               "huffman_only,\n\t\trun_length_encoded and fixed\n");
+}
+
+
+struct compressor gzip_comp_ops = {
+       .init = gzip_init,
+       .compress = gzip_compress,
+       .uncompress = gzip_uncompress,
+       .options = gzip_options,
+       .options_post = gzip_options_post,
+       .dump_options = gzip_dump_options,
+       .extract_options = gzip_extract_options,
+       .display_options = gzip_display_options,
+       .usage = gzip_usage,
+       .id = ZLIB_COMPRESSION,
+       .name = "gzip",
+       .supported = 1
+};
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/gzip_wrapper.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/gzip_wrapper.h
new file mode 100644 (file)
index 0000000..463e9f4
--- /dev/null
@@ -0,0 +1,75 @@
+#ifndef GZIP_WRAPPER_H
+#define GZIP_WRAPPER_H
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2014
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * gzip_wrapper.h
+ *
+ */
+
+#ifndef linux
+#define __BYTE_ORDER BYTE_ORDER
+#define __BIG_ENDIAN BIG_ENDIAN
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#else
+#include <endian.h>
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+extern unsigned int inswap_le16(unsigned short);
+extern unsigned int inswap_le32(unsigned int);
+
+#define SQUASHFS_INSWAP_COMP_OPTS(s) { \
+       (s)->compression_level = inswap_le32((s)->compression_level); \
+       (s)->window_size = inswap_le16((s)->window_size); \
+       (s)->strategy = inswap_le16((s)->strategy); \
+}
+#else
+#define SQUASHFS_INSWAP_COMP_OPTS(s)
+#endif
+
+/* Default compression */
+#define GZIP_DEFAULT_COMPRESSION_LEVEL 9
+#define GZIP_DEFAULT_WINDOW_SIZE 15
+
+struct gzip_comp_opts {
+       int compression_level;
+       short window_size;
+       short strategy;
+};
+
+struct strategy {
+       char *name;
+       int strategy;
+       int selected;
+};
+
+struct gzip_strategy {
+       int strategy;
+       int length;
+       void *buffer;
+};
+
+struct gzip_stream {
+       z_stream stream;
+       int strategies;
+       struct gzip_strategy strategy[0];
+};
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/info.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/info.c
new file mode 100644 (file)
index 0000000..fe23d78
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2013, 2014, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * info.c
+ */
+
+#include <pthread.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include "squashfs_fs.h"
+#include "mksquashfs.h"
+#include "error.h"
+#include "progressbar.h"
+#include "caches-queues-lists.h"
+
+static int silent = 0;
+static struct dir_ent *ent = NULL;
+
+pthread_t info_thread;
+
+
+void disable_info()
+{
+       ent = NULL;
+}
+
+
+void update_info(struct dir_ent *dir_ent)
+{
+       ent = dir_ent;
+}
+
+
+void print_filename()
+{
+       struct dir_ent *dir_ent = ent;
+
+       if(dir_ent == NULL)
+               return;
+
+       if(dir_ent->our_dir->subpath[0] != '\0')
+               INFO("%s/%s\n", dir_ent->our_dir->subpath, dir_ent->name);
+       else
+               INFO("/%s\n", dir_ent->name);
+}
+
+
+void dump_state()
+{
+       disable_progress_bar();
+
+       printf("Queue and Cache status dump\n");
+       printf("===========================\n");
+
+       printf("file buffer queue (reader thread -> deflate thread(s))\n");
+       dump_queue(to_deflate);
+
+       printf("uncompressed fragment queue (reader thread -> fragment"
+                                               " thread(s))\n");
+       dump_queue(to_process_frag);
+
+       printf("processed fragment queue (fragment thread(s) -> main"
+                                               " thread)\n");
+       dump_seq_queue(to_main, 1);
+
+       printf("compressed block queue (deflate thread(s) -> main thread)\n");
+       dump_seq_queue(to_main, 0);
+
+       printf("uncompressed packed fragment queue (main thread -> fragment"
+                                               " deflate thread(s))\n");
+       dump_queue(to_frag);
+
+       if(!reproducible) {
+               printf("locked frag queue (compressed frags waiting while multi-block"
+                                                       " file is written)\n");
+               dump_queue(locked_fragment);
+
+               printf("compressed block queue (main & fragment deflate threads(s) ->"
+                                               " writer thread)\n");
+               dump_queue(to_writer);
+       } else {
+               printf("compressed fragment queue (fragment deflate threads(s) ->"
+                                               "fragment order thread)\n");
+
+               dump_seq_queue(to_order, 0);
+
+               printf("compressed block queue (main & fragment order threads ->"
+                                               " writer thread)\n");
+               dump_queue(to_writer);
+       }
+
+       printf("read cache (uncompressed blocks read by reader thread)\n");
+       dump_cache(reader_buffer);
+
+       printf("block write cache (compressed blocks waiting for the writer"
+                                               " thread)\n");
+       dump_cache(bwriter_buffer);
+       printf("fragment write cache (compressed fragments waiting for the"
+                                               " writer thread)\n");
+       dump_cache(fwriter_buffer);
+
+       printf("fragment cache (frags waiting to be compressed by fragment"
+                                               " deflate thread(s))\n");
+       dump_cache(fragment_buffer);
+
+       printf("fragment reserve cache (avoids pipeline stall if frag cache"
+                                               " full in dup check)\n");
+       dump_cache(reserve_cache);
+
+       enable_progress_bar();
+}
+
+
+void *info_thrd(void *arg)
+{
+       sigset_t sigmask;
+       struct timespec timespec = { .tv_sec = 1, .tv_nsec = 0 };
+       int sig, waiting = 0;
+
+       sigemptyset(&sigmask);
+       sigaddset(&sigmask, SIGQUIT);
+       sigaddset(&sigmask, SIGHUP);
+
+       while(1) {
+               if(waiting)
+                       sig = sigtimedwait(&sigmask, NULL, &timespec);
+               else
+                       sig = sigwaitinfo(&sigmask, NULL);
+
+               if(sig == -1) {
+                       switch(errno) {
+                       case EAGAIN:
+                               /* interval timed out */
+                               waiting = 0;
+                               /* FALLTHROUGH */
+                       case EINTR:
+                               /* if waiting, the wait will be longer, but
+                                  that's OK */
+                               continue;
+                       default:
+                               BAD_ERROR("sigtimedwait/sigwaitinfo failed "
+                                       "because %s\n", strerror(errno));
+                       }
+               }
+
+               if(sig == SIGQUIT && !waiting) {
+                       print_filename();
+
+                       /* set one second interval period, if ^\ received
+                          within then, dump queue and cache status */
+                       waiting = 1;
+               } else
+                       dump_state();
+       }
+}
+
+
+void init_info()
+{
+       pthread_create(&info_thread, NULL, info_thrd, NULL);
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/info.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/info.h
new file mode 100644 (file)
index 0000000..bcf03a2
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef INFO_H
+#define INFO_H
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2013, 2014
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * info.h
+ */
+
+extern void disable_info();
+extern void update_info(struct dir_ent *);
+extern void init_info();
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/lz4_wrapper.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/lz4_wrapper.c
new file mode 100644 (file)
index 0000000..bc9c04d
--- /dev/null
@@ -0,0 +1,286 @@
+/*
+ * Copyright (c) 2013, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * lz4_wrapper.c
+ *
+ * Support for LZ4 compression http://fastcompression.blogspot.com/p/lz4.html
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <lz4.h>
+#include <lz4hc.h>
+
+#include "squashfs_fs.h"
+#include "lz4_wrapper.h"
+#include "compressor.h"
+
+/* LZ4 1.7.0 introduced new functions, and since r131,
+ * the older functions produce deprecated warnings.
+ *
+ * There are still too many distros using older versions
+ * to switch to the newer functions, but, the deprecated
+ * functions may completely disappear.  This is a mess.
+ *
+ * Support both by checking the library version and
+ * using shadow definitions
+ */
+
+/* Earlier (but > 1.7.0) versions don't define this */
+#ifndef LZ4HC_CLEVEL_MAX
+#define LZ4HC_CLEVEL_MAX 12
+#endif
+
+#if LZ4_VERSION_NUMBER >= 10700
+#define COMPRESS(src, dest, size, max)          LZ4_compress_default(src, dest, size, max)
+#define COMPRESS_HC(src, dest, size, max)       LZ4_compress_HC(src, dest, size, max, LZ4HC_CLEVEL_MAX)
+#else
+#define COMPRESS(src, dest, size, max)          LZ4_compress_limitedOutput(src, dest, size, max)
+#define COMPRESS_HC(src, dest, size, max)       LZ4_compressHC_limitedOutput(src, dest, size, max)
+#endif
+
+static int hc = 0;
+
+/*
+ * This function is called by the options parsing code in mksquashfs.c
+ * to parse any -X compressor option.
+ *
+ * This function returns:
+ *     >=0 (number of additional args parsed) on success
+ *     -1 if the option was unrecognised, or
+ *     -2 if the option was recognised, but otherwise bad in
+ *        some way (e.g. invalid parameter)
+ *
+ * Note: this function sets internal compressor state, but does not
+ * pass back the results of the parsing other than success/failure.
+ * The lz4_dump_options() function is called later to get the options in
+ * a format suitable for writing to the filesystem.
+ */
+static int lz4_options(char *argv[], int argc)
+{
+       if(strcmp(argv[0], "-Xhc") == 0) {
+               hc = 1;
+               return 0;
+       }
+
+       return -1;
+}
+
+
+/*
+ * This function is called by mksquashfs to dump the parsed
+ * compressor options in a format suitable for writing to the
+ * compressor options field in the filesystem (stored immediately
+ * after the superblock).
+ *
+ * This function returns a pointer to the compression options structure
+ * to be stored (and the size), or NULL if there are no compression
+ * options
+ *
+ * Currently LZ4 always returns a comp_opts structure, with
+ * the version indicating LZ4_LEGACY stream fomat.  This is to
+ * easily accomodate changes in the kernel code to different
+ * stream formats 
+ */
+static void *lz4_dump_options(int block_size, int *size)
+{
+       static struct lz4_comp_opts comp_opts;
+
+       comp_opts.version = LZ4_LEGACY;
+       comp_opts.flags = hc ? LZ4_HC : 0;
+       SQUASHFS_INSWAP_COMP_OPTS(&comp_opts);
+
+       *size = sizeof(comp_opts);
+       return &comp_opts;
+}
+
+
+/*
+ * This function is a helper specifically for the append mode of
+ * mksquashfs.  Its purpose is to set the internal compressor state
+ * to the stored compressor options in the passed compressor options
+ * structure.
+ *
+ * In effect this function sets up the compressor options
+ * to the same state they were when the filesystem was originally
+ * generated, this is to ensure on appending, the compressor uses
+ * the same compression options that were used to generate the
+ * original filesystem.
+ *
+ * Note, even if there are no compressor options, this function is still
+ * called with an empty compressor structure (size == 0), to explicitly
+ * set the default options, this is to ensure any user supplied
+ * -X options on the appending mksquashfs command line are over-ridden
+ *
+ * This function returns 0 on sucessful extraction of options, and
+ *                     -1 on error
+ */
+static int lz4_extract_options(int block_size, void *buffer, int size)
+{
+       struct lz4_comp_opts *comp_opts = buffer;
+
+       /* we expect a comp_opts structure to be present */
+       if(size < sizeof(*comp_opts))
+               goto failed;
+
+       SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
+
+       /* we expect the stream format to be LZ4_LEGACY */
+       if(comp_opts->version != LZ4_LEGACY) {
+               fprintf(stderr, "lz4: unknown LZ4 version\n");
+               goto failed;
+       }
+
+       /*
+        * Check compression flags, currently only LZ4_HC ("high compression")
+        * can be set.
+        */
+       if(comp_opts->flags == LZ4_HC)
+               hc = 1;
+       else if(comp_opts->flags != 0) {
+               fprintf(stderr, "lz4: unknown LZ4 flags\n");
+               goto failed;
+       }
+
+       return 0;
+
+failed:
+       fprintf(stderr, "lz4: error reading stored compressor options from "
+               "filesystem!\n");
+
+       return -1;
+}
+
+
+/*
+ * This function is a helper specifically for unsquashfs.
+ * Its purpose is to check that the compression options are
+ * understood by this version of LZ4.
+ *
+ * This is important for LZ4 because the format understood by the
+ * Linux kernel may change from the already obsolete legacy format
+ * currently supported.
+ *
+ * If this does happen, then this version of LZ4 will not be able to decode
+ * the newer format.  So we need to check for this.
+ *
+ * This function returns 0 on sucessful checking of options, and
+ *                     -1 on error
+ */
+static int lz4_check_options(int block_size, void *buffer, int size)
+{
+       struct lz4_comp_opts *comp_opts = buffer;
+
+       /* we expect a comp_opts structure to be present */
+       if(size < sizeof(*comp_opts))
+               goto failed;
+
+       SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
+
+       /* we expect the stream format to be LZ4_LEGACY */
+       if(comp_opts->version != LZ4_LEGACY) {
+               fprintf(stderr, "lz4: unknown LZ4 version\n");
+               goto failed;
+       }
+
+       return 0;
+
+failed:
+       fprintf(stderr, "lz4: error reading stored compressor options from "
+               "filesystem!\n");
+       return -1;
+}
+
+
+static void lz4_display_options(void *buffer, int size)
+{
+       struct lz4_comp_opts *comp_opts = buffer;
+
+       /* check passed comp opts struct is of the correct length */
+       if(size < sizeof(*comp_opts))
+               goto failed;
+
+       SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
+
+       /* we expect the stream format to be LZ4_LEGACY */
+       if(comp_opts->version != LZ4_LEGACY) {
+               fprintf(stderr, "lz4: unknown LZ4 version\n");
+               goto failed;
+       }
+
+       /*
+        * Check compression flags, currently only LZ4_HC ("high compression")
+        * can be set.
+        */
+       if(comp_opts->flags & ~LZ4_FLAGS_MASK) {
+               fprintf(stderr, "lz4: unknown LZ4 flags\n");
+               goto failed;
+       }
+
+       if(comp_opts->flags & LZ4_HC)
+               printf("\tHigh Compression option specified (-Xhc)\n");
+
+       return;
+
+failed:
+       fprintf(stderr, "lz4: error reading stored compressor options from "
+               "filesystem!\n");
+}      
+
+
+static int lz4_compress(void *strm, void *dest, void *src,  int size,
+       int block_size, int *error)
+{
+       return 0;
+}
+
+
+static int lz4_uncompress(void *dest, void *src, int size, int outsize,
+       int *error)
+{
+       int res = LZ4_decompress_safe(src, dest, size, outsize);
+       if(res < 0) {
+               *error = res;
+               return -1;
+       }
+
+       return res;
+}
+
+
+static void lz4_usage()
+{
+       fprintf(stderr, "\t  -Xhc\n");
+       fprintf(stderr, "\t\tCompress using LZ4 High Compression\n");
+}
+
+
+struct compressor lz4_comp_ops = {
+       .compress = lz4_compress,
+       .uncompress = lz4_uncompress,
+       .options = lz4_options,
+       .dump_options = lz4_dump_options,
+       .extract_options = lz4_extract_options,
+       .check_options = lz4_check_options,
+       .display_options = lz4_display_options,
+       .usage = lz4_usage,
+       .id = LZ4_COMPRESSION,
+       .name = "lz4",
+       .supported = 1
+};
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/lz4_wrapper.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/lz4_wrapper.h
new file mode 100644 (file)
index 0000000..d6638a5
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef LZ4_WRAPPER_H
+#define LZ4_WRAPPER_H
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * lz4_wrapper.h
+ *
+ */
+
+#ifndef linux
+#define __BYTE_ORDER BYTE_ORDER
+#define __BIG_ENDIAN BIG_ENDIAN
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#else
+#include <endian.h>
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+extern unsigned int inswap_le32(unsigned int);
+
+#define SQUASHFS_INSWAP_COMP_OPTS(s) { \
+       (s)->version = inswap_le32((s)->version); \
+       (s)->flags = inswap_le32((s)->flags); \
+}
+#else
+#define SQUASHFS_INSWAP_COMP_OPTS(s)
+#endif
+
+/*
+ * Define the various stream formats recognised.
+ * Currently omly legacy stream format is supported by the
+ * kernel
+ */
+#define LZ4_LEGACY     1
+#define LZ4_FLAGS_MASK 1
+
+/* Define the compression flags recognised. */
+#define LZ4_HC         1
+
+struct lz4_comp_opts {
+       int version;
+       int flags;
+};
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/lzma_wrapper.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/lzma_wrapper.c
new file mode 100644 (file)
index 0000000..1e76733
--- /dev/null
@@ -0,0 +1,78 @@
+/*
+ * Copyright (c) 2009, 2010, 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * lzma_wrapper.c
+ *
+ * Support for LZMA1 compression using LZMA SDK (4.65 used in
+ * development, other versions may work) http://www.7-zip.org/sdk.html
+ */
+
+#include <LzmaLib.h>
+
+#include "squashfs_fs.h"
+#include "compressor.h"
+
+#define LZMA_HEADER_SIZE       (LZMA_PROPS_SIZE + 8)
+
+static int lzma_compress(void *strm, void *dest, void *src, int size, int block_size,
+               int *error)
+{
+       return 0;
+}
+
+
+static int lzma_uncompress(void *dest, void *src, int size, int outsize,
+       int *error)
+{
+       unsigned char *s = src;
+       size_t outlen, inlen = size - LZMA_HEADER_SIZE;
+       int res;
+
+       outlen = s[LZMA_PROPS_SIZE] |
+               (s[LZMA_PROPS_SIZE + 1] << 8) |
+               (s[LZMA_PROPS_SIZE + 2] << 16) |
+               (s[LZMA_PROPS_SIZE + 3] << 24);
+
+       if(outlen > outsize) {
+               *error = 0;
+               return -1;
+       }
+
+       res = LzmaUncompress(dest, &outlen, src + LZMA_HEADER_SIZE, &inlen, src,
+               LZMA_PROPS_SIZE);
+       
+       if(res == SZ_OK)
+               return outlen;
+       else {
+               *error = res;
+               return -1;
+       }
+}
+
+
+struct compressor lzma_comp_ops = {
+       .init = NULL,
+       .compress = lzma_compress,
+       .uncompress = lzma_uncompress,
+       .options = NULL,
+       .usage = NULL,
+       .id = LZMA_COMPRESSION,
+       .name = "lzma",
+       .supported = 1
+};
+
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/lzma_xz_wrapper.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/lzma_xz_wrapper.c
new file mode 100644 (file)
index 0000000..55a6813
--- /dev/null
@@ -0,0 +1,163 @@
+/*
+ * Copyright (c) 2010, 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * lzma_xz_wrapper.c
+ *
+ * Support for LZMA1 compression using XZ Utils liblzma http://tukaani.org/xz/
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <lzma.h>
+
+#include "squashfs_fs.h"
+#include "compressor.h"
+
+#define LZMA_PROPS_SIZE 5
+#define LZMA_UNCOMP_SIZE 8
+#define LZMA_HEADER_SIZE (LZMA_PROPS_SIZE + LZMA_UNCOMP_SIZE)
+
+#define LZMA_OPTIONS 5
+#define MEMLIMIT (32 * 1024 * 1024)
+
+static int lzma_compress(void *dummy, void *dest, void *src,  int size,
+       int block_size, int *error)
+{
+       unsigned char *d = (unsigned char *) dest;
+       lzma_options_lzma opt;
+       lzma_stream strm = LZMA_STREAM_INIT;
+       int res;
+
+       lzma_lzma_preset(&opt, LZMA_OPTIONS);
+       opt.dict_size = block_size;
+
+       res = lzma_alone_encoder(&strm, &opt);
+       if(res != LZMA_OK) {
+               lzma_end(&strm);
+               goto failed;
+       }
+
+       strm.next_out = dest;
+       strm.avail_out = block_size;
+       strm.next_in = src;
+       strm.avail_in = size;
+
+       res = lzma_code(&strm, LZMA_FINISH);
+       lzma_end(&strm);
+
+       if(res == LZMA_STREAM_END) {
+               /*
+                * Fill in the 8 byte little endian uncompressed size field in
+                * the LZMA header.  8 bytes is excessively large for squashfs
+                * but this is the standard LZMA header and which is expected by
+                * the kernel code
+                */
+
+               d[LZMA_PROPS_SIZE] = size & 255;
+               d[LZMA_PROPS_SIZE + 1] = (size >> 8) & 255;
+               d[LZMA_PROPS_SIZE + 2] = (size >> 16) & 255;
+               d[LZMA_PROPS_SIZE + 3] = (size >> 24) & 255;
+               d[LZMA_PROPS_SIZE + 4] = 0;
+               d[LZMA_PROPS_SIZE + 5] = 0;
+               d[LZMA_PROPS_SIZE + 6] = 0;
+               d[LZMA_PROPS_SIZE + 7] = 0;
+
+               return (int) strm.total_out;
+       }
+
+       if(res == LZMA_OK)
+               /*
+                * Output buffer overflow.  Return out of buffer space
+                */
+               return 0;
+
+failed:
+       /*
+        * All other errors return failure, with the compressor
+        * specific error code in *error
+        */
+       *error = res;
+       return -1;
+}
+
+
+static int lzma_uncompress(void *dest, void *src, int size, int outsize,
+       int *error)
+{
+       lzma_stream strm = LZMA_STREAM_INIT;
+       int uncompressed_size = 0, res;
+       unsigned char lzma_header[LZMA_HEADER_SIZE];
+
+       res = lzma_alone_decoder(&strm, MEMLIMIT);
+       if(res != LZMA_OK) {
+               lzma_end(&strm);
+               goto failed;
+       }
+
+       memcpy(lzma_header, src, LZMA_HEADER_SIZE);
+       uncompressed_size = lzma_header[LZMA_PROPS_SIZE] |
+               (lzma_header[LZMA_PROPS_SIZE + 1] << 8) |
+               (lzma_header[LZMA_PROPS_SIZE + 2] << 16) |
+               (lzma_header[LZMA_PROPS_SIZE + 3] << 24);
+
+       if(uncompressed_size > outsize) {
+               res = 0;
+               goto failed;
+       }
+
+       memset(lzma_header + LZMA_PROPS_SIZE, 255, LZMA_UNCOMP_SIZE);
+
+       strm.next_out = dest;
+       strm.avail_out = outsize;
+       strm.next_in = lzma_header;
+       strm.avail_in = LZMA_HEADER_SIZE;
+
+       res = lzma_code(&strm, LZMA_RUN);
+
+       if(res != LZMA_OK || strm.avail_in != 0) {
+               lzma_end(&strm);
+               goto failed;
+       }
+
+       strm.next_in = src + LZMA_HEADER_SIZE;
+       strm.avail_in = size - LZMA_HEADER_SIZE;
+
+       res = lzma_code(&strm, LZMA_FINISH);
+       lzma_end(&strm);
+
+       if(res == LZMA_STREAM_END || (res == LZMA_OK &&
+               strm.total_out >= uncompressed_size && strm.avail_in == 0))
+               return uncompressed_size;
+
+failed:
+       *error = res;
+       return -1;
+}
+
+
+struct compressor lzma_comp_ops = {
+       .init = NULL,
+       .compress = lzma_compress,
+       .uncompress = lzma_uncompress,
+       .options = NULL,
+       .usage = NULL,
+       .id = LZMA_COMPRESSION,
+       .name = "lzma",
+       .supported = 1
+};
+
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/lzo_wrapper.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/lzo_wrapper.c
new file mode 100644 (file)
index 0000000..4d7c3db
--- /dev/null
@@ -0,0 +1,365 @@
+/*
+ * Copyright (c) 2013, 2014
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * lzo_wrapper.c
+ *
+ * Support for LZO compression http://www.oberhumer.com/opensource/lzo
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <lzo/lzoconf.h>
+#include <lzo/lzo1x.h>
+
+#include "squashfs_fs.h"
+#include "lzo_wrapper.h"
+#include "compressor.h"
+
+static struct lzo_algorithm lzo[] = {
+       { "lzo1x_1", LZO1X_1_MEM_COMPRESS, lzo1x_1_compress },
+       { "lzo1x_1_11", LZO1X_1_11_MEM_COMPRESS, lzo1x_1_11_compress },
+       { "lzo1x_1_12", LZO1X_1_12_MEM_COMPRESS, lzo1x_1_12_compress },
+       { "lzo1x_1_15", LZO1X_1_15_MEM_COMPRESS, lzo1x_1_15_compress },
+       { "lzo1x_999", LZO1X_999_MEM_COMPRESS, lzo1x_999_wrapper },
+       { NULL, 0, NULL } 
+};
+
+/* default LZO compression algorithm and compression level */
+static int algorithm = SQUASHFS_LZO1X_999;
+static int compression_level = SQUASHFS_LZO1X_999_COMP_DEFAULT;
+
+/* user specified compression level */
+static int user_comp_level = -1;
+
+
+/*
+ * This function is called by the options parsing code in mksquashfs.c
+ * to parse any -X compressor option.
+ *
+ * This function returns:
+ *     >=0 (number of additional args parsed) on success
+ *     -1 if the option was unrecognised, or
+ *     -2 if the option was recognised, but otherwise bad in
+ *        some way (e.g. invalid parameter)
+ *
+ * Note: this function sets internal compressor state, but does not
+ * pass back the results of the parsing other than success/failure.
+ * The lzo_dump_options() function is called later to get the options in
+ * a format suitable for writing to the filesystem.
+ */
+static int lzo_options(char *argv[], int argc)
+{
+    (void)argv;
+    (void)argc;
+       return 1;
+}
+
+
+/*
+ * This function is called after all options have been parsed.
+ * It is used to do post-processing on the compressor options using
+ * values that were not expected to be known at option parse time.
+ *
+ * In this case the LZO algorithm may not be known until after the
+ * compression level has been set (-Xalgorithm used after -Xcompression-level)
+ *
+ * This function returns 0 on successful post processing, or
+ *                     -1 on error
+ */
+static int lzo_options_post(int block_size)
+{
+       /*
+        * Use of compression level only makes sense for
+        * LZO1X_999 algorithm
+        */
+       if(user_comp_level != -1) {
+               if(algorithm != SQUASHFS_LZO1X_999) {
+                       fprintf(stderr, "lzo: -Xcompression-level not "
+                               "supported by selected %s algorithm\n",
+                               lzo[algorithm].name);
+                       fprintf(stderr, "lzo: -Xcompression-level is only "
+                               "applicable for the lzo1x_999 algorithm\n");
+                       goto failed;
+               }
+               compression_level = user_comp_level;
+       }
+
+       return 0;
+
+failed:
+       return -1;
+}
+
+
+/*
+ * This function is called by mksquashfs to dump the parsed
+ * compressor options in a format suitable for writing to the
+ * compressor options field in the filesystem (stored immediately
+ * after the superblock).
+ *
+ * This function returns a pointer to the compression options structure
+ * to be stored (and the size), or NULL if there are no compression
+ * options
+ *
+ */
+static void *lzo_dump_options(int block_size, int *size)
+{
+       static struct lzo_comp_opts comp_opts;
+
+       /*
+        * If default compression options of SQUASHFS_LZO1X_999 and
+        * compression level of SQUASHFS_LZO1X_999_COMP_DEFAULT then
+        * don't store a compression options structure (this is compatible
+        * with the legacy implementation of LZO for Squashfs)
+        */
+       if(algorithm == SQUASHFS_LZO1X_999 &&
+                       compression_level == SQUASHFS_LZO1X_999_COMP_DEFAULT)
+               return NULL;
+
+       comp_opts.algorithm = algorithm;
+       comp_opts.compression_level = algorithm == SQUASHFS_LZO1X_999 ?
+               compression_level : 0;
+
+       SQUASHFS_INSWAP_COMP_OPTS(&comp_opts);
+
+       *size = sizeof(comp_opts);
+       return &comp_opts;
+}
+
+
+/*
+ * This function is a helper specifically for the append mode of
+ * mksquashfs.  Its purpose is to set the internal compressor state
+ * to the stored compressor options in the passed compressor options
+ * structure.
+ *
+ * In effect this function sets up the compressor options
+ * to the same state they were when the filesystem was originally
+ * generated, this is to ensure on appending, the compressor uses
+ * the same compression options that were used to generate the
+ * original filesystem.
+ *
+ * Note, even if there are no compressor options, this function is still
+ * called with an empty compressor structure (size == 0), to explicitly
+ * set the default options, this is to ensure any user supplied
+ * -X options on the appending mksquashfs command line are over-ridden
+ *
+ * This function returns 0 on sucessful extraction of options, and
+ *                     -1 on error
+ */
+static int lzo_extract_options(int block_size, void *buffer, int size)
+{
+       struct lzo_comp_opts *comp_opts = buffer;
+
+       if(size == 0) {
+               /* Set default values */
+               algorithm = SQUASHFS_LZO1X_999;
+               compression_level = SQUASHFS_LZO1X_999_COMP_DEFAULT;
+               return 0;
+       }
+
+       /* we expect a comp_opts structure of sufficient size to be present */
+       if(size < sizeof(*comp_opts))
+               goto failed;
+
+       SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
+
+       /* Check comp_opts structure for correctness */
+       switch(comp_opts->algorithm) {
+       case SQUASHFS_LZO1X_1:
+       case SQUASHFS_LZO1X_1_11:
+       case SQUASHFS_LZO1X_1_12:
+       case SQUASHFS_LZO1X_1_15:
+               if(comp_opts->compression_level != 0) {
+                       fprintf(stderr, "lzo: bad compression level in "
+                               "compression options structure\n");
+                       goto failed;
+               }
+               break;
+       case SQUASHFS_LZO1X_999:
+               if(comp_opts->compression_level < 1 ||
+                               comp_opts->compression_level > 9) {
+                       fprintf(stderr, "lzo: bad compression level in "
+                               "compression options structure\n");
+                       goto failed;
+               }
+               compression_level = comp_opts->compression_level;
+               break;
+       default:
+               fprintf(stderr, "lzo: bad algorithm in compression options "
+                               "structure\n");
+                       goto failed;
+       }
+
+       algorithm = comp_opts->algorithm;
+
+       return 0;
+
+failed:
+       fprintf(stderr, "lzo: error reading stored compressor options from "
+               "filesystem!\n");
+
+       return -1;
+}
+
+
+static void lzo_display_options(void *buffer, int size)
+{
+       struct lzo_comp_opts *comp_opts = buffer;
+
+       /* we expect a comp_opts structure of sufficient size to be present */
+       if(size < sizeof(*comp_opts))
+               goto failed;
+
+       SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
+
+       /* Check comp_opts structure for correctness */
+       switch(comp_opts->algorithm) {
+       case SQUASHFS_LZO1X_1:
+       case SQUASHFS_LZO1X_1_11:
+       case SQUASHFS_LZO1X_1_12:
+       case SQUASHFS_LZO1X_1_15:
+               printf("\talgorithm %s\n", lzo[comp_opts->algorithm].name);
+               break;
+       case SQUASHFS_LZO1X_999:
+               if(comp_opts->compression_level < 1 ||
+                               comp_opts->compression_level > 9) {
+                       fprintf(stderr, "lzo: bad compression level in "
+                               "compression options structure\n");
+                       goto failed;
+               }
+               printf("\talgorithm %s\n", lzo[comp_opts->algorithm].name);
+               printf("\tcompression level %d\n",
+                                               comp_opts->compression_level);
+               break;
+       default:
+               fprintf(stderr, "lzo: bad algorithm in compression options "
+                               "structure\n");
+                       goto failed;
+       }
+
+       return;
+
+failed:
+       fprintf(stderr, "lzo: error reading stored compressor options from "
+               "filesystem!\n");
+}      
+
+
+/*
+ * This function is called by mksquashfs to initialise the
+ * compressor, before compress() is called.
+ *
+ * This function returns 0 on success, and
+ *                     -1 on error
+ */
+static int squashfs_lzo_init(void **strm, int block_size, int datablock)
+{
+       struct lzo_stream *stream;
+
+       stream = *strm = malloc(sizeof(struct lzo_stream));
+       if(stream == NULL)
+               goto failed;
+
+       stream->workspace = malloc(lzo[algorithm].size);
+       if(stream->workspace == NULL)
+               goto failed2;
+
+       stream->buffer = malloc(LZO_MAX_EXPANSION(block_size));
+       if(stream->buffer != NULL)
+               return 0;
+
+       free(stream->workspace);
+failed2:
+       free(stream);
+failed:
+       return -1;
+}
+
+
+static int lzo_compress(void *strm, void *dest, void *src,  int size,
+       int block_size, int *error)
+{
+    
+       return 0;
+}
+
+
+static int lzo_uncompress(void *dest, void *src, int size, int outsize,
+       int *error)
+{
+       int res;
+       lzo_uint outlen = outsize;
+
+       res = lzo1x_decompress_safe(src, size, dest, &outlen, NULL);
+       if(res != LZO_E_OK) {
+               *error = res;
+               return -1;
+       }
+
+       return outlen;
+}
+
+
+static void lzo_usage()
+{
+       int i;
+
+       fprintf(stderr, "\t  -Xalgorithm <algorithm>\n");
+       fprintf(stderr, "\t\tWhere <algorithm> is one of:\n");
+
+       for(i = 0; lzo[i].name; i++)
+               fprintf(stderr, "\t\t\t%s%s\n", lzo[i].name,
+                               i == SQUASHFS_LZO1X_999 ? " (default)" : "");
+
+       fprintf(stderr, "\t  -Xcompression-level <compression-level>\n");
+       fprintf(stderr, "\t\t<compression-level> should be 1 .. 9 (default "
+               "%d)\n", SQUASHFS_LZO1X_999_COMP_DEFAULT);
+       fprintf(stderr, "\t\tOnly applies to lzo1x_999 algorithm\n");
+}
+
+
+/*
+ * Helper function for lzo1x_999 compression algorithm.
+ * All other lzo1x_xxx compressors do not take a compression level,
+ * so we need to wrap lzo1x_999 to pass the compression level which
+ * is applicable to it
+ */
+int lzo1x_999_wrapper(const lzo_bytep src, lzo_uint src_len, lzo_bytep dst,
+       lzo_uintp compsize, lzo_voidp workspace)
+{
+       return lzo1x_999_compress_level(src, src_len, dst, compsize,
+               workspace, NULL, 0, 0, compression_level);
+}
+
+
+struct compressor lzo_comp_ops = {
+       .init = squashfs_lzo_init,
+       .compress = lzo_compress,
+       .uncompress = lzo_uncompress,
+       .options = lzo_options,
+       .options_post = lzo_options_post,
+       .dump_options = lzo_dump_options,
+       .extract_options = lzo_extract_options,
+       .display_options = lzo_display_options,
+       .usage = lzo_usage,
+       .id = LZO_COMPRESSION,
+       .name = "lzo",
+       .supported = 1
+};
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/lzo_wrapper.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/lzo_wrapper.h
new file mode 100644 (file)
index 0000000..804e53c
--- /dev/null
@@ -0,0 +1,78 @@
+#ifndef LZO_WRAPPER_H
+#define LZO_WRAPPER_H
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * lzo_wrapper.h
+ *
+ */
+
+#ifndef linux
+#define __BYTE_ORDER BYTE_ORDER
+#define __BIG_ENDIAN BIG_ENDIAN
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#else
+#include <endian.h>
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+extern unsigned int inswap_le32(unsigned int);
+
+#define SQUASHFS_INSWAP_COMP_OPTS(s) { \
+       (s)->algorithm = inswap_le32((s)->algorithm); \
+       (s)->compression_level = inswap_le32((s)->compression_level); \
+}
+#else
+#define SQUASHFS_INSWAP_COMP_OPTS(s)
+#endif
+
+/* Define the compression flags recognised. */
+#define SQUASHFS_LZO1X_1       0
+#define SQUASHFS_LZO1X_1_11    1
+#define SQUASHFS_LZO1X_1_12    2
+#define SQUASHFS_LZO1X_1_15    3
+#define SQUASHFS_LZO1X_999     4
+
+/* Default compression level used by SQUASHFS_LZO1X_999 */
+#define SQUASHFS_LZO1X_999_COMP_DEFAULT        8
+
+struct lzo_comp_opts {
+       int algorithm;
+       int compression_level;
+};
+
+struct lzo_algorithm {
+       char *name;
+       int size;
+       int (*compress) (const lzo_bytep, lzo_uint, lzo_bytep, lzo_uintp,
+               lzo_voidp);
+};
+
+struct lzo_stream {
+       void *workspace;
+       void *buffer;
+};
+
+#define LZO_MAX_EXPANSION(size)        (size + (size / 16) + 64 + 3)
+
+int lzo1x_999_wrapper(const lzo_bytep, lzo_uint, lzo_bytep, lzo_uintp,
+               lzo_voidp);
+
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/mksquashfs.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/mksquashfs.c
new file mode 100644 (file)
index 0000000..a45b77f
--- /dev/null
@@ -0,0 +1,6372 @@
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,
+ * 2012, 2013, 2014, 2017, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * mksquashfs.c
+ */
+
+#define FALSE 0
+#define TRUE 1
+#define MAX_LINE 16384
+
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <stddef.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <sys/sysmacros.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <setjmp.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <pthread.h>
+#include <regex.h>
+#include <sys/wait.h>
+#include <limits.h>
+#include <ctype.h>
+#include <sys/sysinfo.h>
+
+#ifndef linux
+#define __BYTE_ORDER BYTE_ORDER
+#define __BIG_ENDIAN BIG_ENDIAN
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#include <sys/sysctl.h>
+#else
+#include <endian.h>
+#include <sys/sysinfo.h>
+#endif
+
+#include "squashfs_fs.h"
+#include "squashfs_swap.h"
+#include "mksquashfs.h"
+#include "sort.h"
+#include "pseudo.h"
+#include "compressor.h"
+#include "xattr.h"
+#include "action.h"
+#include "error.h"
+#include "progressbar.h"
+#include "info.h"
+#include "caches-queues-lists.h"
+#include "read_fs.h"
+#include "restore.h"
+#include "process_fragments.h"
+#include "fnmatch_compat.h"
+
+int delete = FALSE;
+int quiet = FALSE;
+int fd;
+struct squashfs_super_block sBlk;
+
+/* filesystem flags for building */
+int comp_opts = FALSE;
+int no_xattrs = XATTR_DEF;
+int noX = FALSE;
+int duplicate_checking = TRUE;
+int noF = FALSE;
+int no_fragments = FALSE;
+int always_use_fragments = FALSE;
+int noI = FALSE;
+int noId = FALSE;
+int noD = FALSE;
+int silent = TRUE;
+int exportable = TRUE;
+int sparse_files = TRUE;
+int old_exclude = TRUE;
+int use_regex = FALSE;
+int nopad = FALSE;
+int exit_on_error = FALSE;
+long long start_offset = 0;
+
+long long global_uid = -1, global_gid = -1;
+
+/* superblock attributes */
+int block_size = SQUASHFS_FILE_SIZE, block_log;
+unsigned int id_count = 0;
+int file_count = 0, sym_count = 0, dev_count = 0, dir_count = 0, fifo_count = 0,
+       sock_count = 0;
+
+/* write position within data section */
+long long bytes = 0, total_bytes = 0;
+
+/* in memory directory table - possibly compressed */
+char *directory_table = NULL;
+unsigned int directory_bytes = 0, directory_size = 0, total_directory_bytes = 0;
+
+/* cached directory table */
+char *directory_data_cache = NULL;
+unsigned int directory_cache_bytes = 0, directory_cache_size = 0;
+
+/* in memory inode table - possibly compressed */
+char *inode_table = NULL;
+unsigned int inode_bytes = 0, inode_size = 0, total_inode_bytes = 0;
+
+/* cached inode table */
+char *data_cache = NULL;
+unsigned int cache_bytes = 0, cache_size = 0, inode_count = 0;
+
+/* inode lookup table */
+squashfs_inode *inode_lookup_table = NULL;
+
+/* in memory directory data */
+#define I_COUNT_SIZE           128
+#define DIR_ENTRIES            32
+#define INODE_HASH_SIZE                65536
+#define INODE_HASH_MASK                (INODE_HASH_SIZE - 1)
+#define INODE_HASH(dev, ino)   (ino & INODE_HASH_MASK)
+
+struct cached_dir_index {
+       struct squashfs_dir_index       index;
+       char                            *name;
+};
+
+struct directory {
+       unsigned int            start_block;
+       unsigned int            size;
+       unsigned char           *buff;
+       unsigned char           *p;
+       unsigned int            entry_count;
+       unsigned char           *entry_count_p;
+       unsigned int            i_count;
+       unsigned int            i_size;
+       struct cached_dir_index *index;
+       unsigned char           *index_count_p;
+       unsigned int            inode_number;
+};
+
+struct inode_info *inode_info[INODE_HASH_SIZE];
+
+/* hash tables used to do fast duplicate searches in duplicate check */
+struct file_info *dupl[65536];
+int dup_files = 0;
+
+/* exclude file handling */
+/* list of exclude dirs/files */
+struct exclude_info {
+       dev_t                   st_dev;
+       ino_t                   st_ino;
+};
+
+#define EXCLUDE_SIZE 8192
+int exclude = 0;
+struct exclude_info *exclude_paths = NULL;
+int old_excluded(char *filename, struct stat *buf);
+
+struct path_entry {
+       char *name;
+       regex_t *preg;
+       struct pathname *paths;
+};
+
+struct pathname {
+       int names;
+       struct path_entry *name;
+};
+
+struct pathnames {
+       int count;
+       struct pathname *path[0];
+};
+#define PATHS_ALLOC_SIZE 10
+
+struct pathnames *paths = NULL;
+struct pathname *path = NULL;
+struct pathname *stickypath = NULL;
+int excluded(char *name, struct pathnames *paths, struct pathnames **new);
+
+int fragments = 0;
+
+#define FRAG_SIZE 32768
+
+struct squashfs_fragment_entry *fragment_table = NULL;
+int fragments_outstanding = 0;
+
+int fragments_locked = FALSE;
+
+/* current inode number for directories and non directories */
+unsigned int inode_no = 1;
+unsigned int root_inode_number = 0;
+
+/* list of source dirs/files */
+int source = 0;
+char **source_path;
+
+/* list of root directory entries read from original filesystem */
+int old_root_entries = 0;
+struct old_root_entry_info {
+       char                    *name;
+       struct inode_info       inode;
+};
+struct old_root_entry_info *old_root_entry;
+
+/* restore orignal filesystem state if appending to existing filesystem is
+ * cancelled */
+int appending = FALSE;
+char *sdata_cache, *sdirectory_data_cache, *sdirectory_compressed;
+
+long long sbytes, stotal_bytes;
+
+unsigned int sinode_bytes, scache_bytes, sdirectory_bytes,
+       sdirectory_cache_bytes, sdirectory_compressed_bytes,
+       stotal_inode_bytes, stotal_directory_bytes,
+       sinode_count = 0, sfile_count, ssym_count, sdev_count,
+       sdir_count, sfifo_count, ssock_count, sdup_files;
+int sfragments;
+int threads;
+
+/* flag whether destination file is a block device */
+int block_device = FALSE;
+
+/* flag indicating whether files are sorted using sort list(s) */
+int sorted = FALSE;
+
+/* save destination file name for deleting on error */
+char *destination_file = NULL;
+
+/* recovery file for abnormal exit on appending */
+char *recovery_file = NULL;
+int recover = TRUE;
+
+struct id *id_hash_table[ID_ENTRIES];
+struct id *id_table[SQUASHFS_IDS], *sid_table[SQUASHFS_IDS];
+unsigned int uid_count = 0, guid_count = 0;
+unsigned int sid_count = 0, suid_count = 0, sguid_count = 0;
+
+struct cache *reader_buffer, *fragment_buffer, *reserve_cache;
+struct cache *bwriter_buffer, *fwriter_buffer;
+struct queue *to_reader, *to_deflate, *to_writer, *from_writer,
+       *to_frag, *locked_fragment, *to_process_frag;
+struct seq_queue *to_main;
+pthread_t reader_thread, writer_thread, main_thread;
+pthread_t *deflator_thread, *frag_deflator_thread, *frag_thread;
+pthread_t *restore_thread = NULL;
+pthread_mutex_t        fragment_mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t        pos_mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_mutex_t        dup_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+/* reproducible image queues and threads */
+struct seq_queue *to_order;
+pthread_t order_thread;
+pthread_cond_t fragment_waiting = PTHREAD_COND_INITIALIZER;
+
+int reproducible = REP_DEF;
+
+/* Root mode option */
+int root_mode_opt = FALSE;
+mode_t root_mode;
+
+/* Time value over-ride options */
+unsigned int mkfs_time;
+int mkfs_time_opt = FALSE;
+
+unsigned int all_time;
+int all_time_opt = FALSE;
+int clamping = TRUE;
+
+/* user options that control parallelisation */
+int processors = -1;
+int bwriter_size;
+
+/* compression operations */
+struct compressor *comp = NULL;
+int compressor_opt_parsed = FALSE;
+void *stream = NULL;
+
+/* xattr stats */
+unsigned int xattr_bytes = 0, total_xattr_bytes = 0;
+
+/* fragment to file mapping used when appending */
+int append_fragments = 0;
+struct append_file **file_mapping;
+
+/* root of the in-core directory structure */
+struct dir_info *root_dir;
+
+/* log file */
+FILE *log_fd;
+int logging=FALSE;
+
+static char *read_from_disk(long long start, unsigned int avail_bytes);
+void add_old_root_entry(char *name, squashfs_inode inode, int inode_number,
+       int type);
+struct file_info *duplicate(long long file_size, long long bytes,
+       unsigned int **block_list, long long *start, struct fragment **fragment,
+       struct file_buffer *file_buffer, int blocks, unsigned short checksum,
+       int checksum_flag);
+struct dir_info *dir_scan1(char *, char *, struct pathnames *,
+       struct dir_ent *(_readdir)(struct dir_info *), int);
+void dir_scan2(struct dir_info *dir, struct pseudo *pseudo);
+void dir_scan3(struct dir_info *dir);
+void dir_scan4(struct dir_info *dir);
+void dir_scan5(struct dir_info *dir);
+void dir_scan6(struct dir_info *dir);
+void dir_scan7(squashfs_inode *inode, struct dir_info *dir_info);
+struct file_info *add_non_dup(long long file_size, long long bytes,
+       unsigned int *block_list, long long start, struct fragment *fragment,
+       unsigned short checksum, unsigned short fragment_checksum,
+       int checksum_flag, int checksum_frag_flag);
+long long generic_write_table(int, void *, int, void *, int);
+void restorefs();
+struct dir_info *scan1_opendir(char *pathname, char *subpath, int depth);
+void write_filesystem_tables(struct squashfs_super_block *sBlk, int nopad);
+unsigned short get_checksum_mem(char *buff, int bytes);
+void check_usable_phys_mem(int total_mem);
+
+
+void prep_exit()
+{
+       if(restore_thread) {
+               if(pthread_self() == *restore_thread) {
+                       /*
+                        * Recursive failure when trying to restore filesystem!
+                        * Nothing to do except to exit, otherwise we'll just
+                        * appear to hang.  The user should be able to restore
+                        * from the recovery file (which is why it was added, in
+                        * case of catastrophic failure in Mksquashfs)
+                        */
+                       exit(1);
+               } else {
+                       /* signal the restore thread to restore */
+                       pthread_kill(*restore_thread, SIGUSR1);
+                       pthread_exit(NULL);
+               }
+       } else if(delete) {
+               if(destination_file && !block_device)
+                       unlink(destination_file);
+       } else if(recovery_file)
+               unlink(recovery_file);
+}
+
+
+int add_overflow(int a, int b)
+{
+       return (INT_MAX - a) < b;
+}
+
+
+int shift_overflow(int a, int shift)
+{
+       return (INT_MAX >> shift) < a;
+}
+
+int multiply_overflow(int a, int multiplier)
+{
+       return (INT_MAX / multiplier) < a;
+}
+
+
+int multiply_overflowll(long long a, int multiplier)
+{
+       return (LLONG_MAX / multiplier) < a;
+}
+
+
+#define MKINODE(A)     ((squashfs_inode)(((squashfs_inode) inode_bytes << 16) \
+                       + (((char *)A) - data_cache)))
+
+
+void restorefs()
+{
+       ERROR("Exiting - restoring original filesystem!\n\n");
+
+       bytes = sbytes;
+       memcpy(data_cache, sdata_cache, cache_bytes = scache_bytes);
+       memcpy(directory_data_cache, sdirectory_data_cache,
+               sdirectory_cache_bytes);
+       directory_cache_bytes = sdirectory_cache_bytes;
+       inode_bytes = sinode_bytes;
+       directory_bytes = sdirectory_bytes;
+       memcpy(directory_table + directory_bytes, sdirectory_compressed,
+               sdirectory_compressed_bytes);
+       directory_bytes += sdirectory_compressed_bytes;
+       total_bytes = stotal_bytes;
+       total_inode_bytes = stotal_inode_bytes;
+       total_directory_bytes = stotal_directory_bytes;
+       inode_count = sinode_count;
+       file_count = sfile_count;
+       sym_count = ssym_count;
+       dev_count = sdev_count;
+       dir_count = sdir_count;
+       fifo_count = sfifo_count;
+       sock_count = ssock_count;
+       dup_files = sdup_files;
+       fragments = sfragments;
+       id_count = sid_count;
+       restore_xattrs();
+       write_filesystem_tables(&sBlk, nopad);
+       exit(1);
+}
+
+
+void sighandler()
+{
+       EXIT_MKSQUASHFS();
+}
+
+
+int mangle2(void *strm, char *d, char *s, int size,
+       int block_size, int uncompressed, int data_block)
+{
+       int error, c_byte = 0;
+
+       if(!uncompressed) {
+               c_byte = compressor_compress(comp, strm, d, s, size, block_size,
+                        &error);
+               if(c_byte == -1)
+                       BAD_ERROR("mangle2:: %s compress failed with error "
+                               "code %d\n", comp->name, error);
+       }
+
+       if(c_byte == 0 || c_byte >= size) {
+               memcpy(d, s, size);
+               return size | (data_block ? SQUASHFS_COMPRESSED_BIT_BLOCK :
+                       SQUASHFS_COMPRESSED_BIT);
+       }
+
+       return c_byte;
+}
+
+
+int mangle(char *d, char *s, int size, int block_size,
+       int uncompressed, int data_block)
+{
+       return mangle2(stream, d, s, size, block_size, uncompressed,
+               data_block);
+}
+
+
+void *get_inode(int req_size)
+{
+       int data_space;
+       unsigned short c_byte;
+
+       while(cache_bytes >= SQUASHFS_METADATA_SIZE) {
+               if((inode_size - inode_bytes) <
+                               ((SQUASHFS_METADATA_SIZE << 1)) + 2) {
+                       void *it = realloc(inode_table, inode_size +
+                               (SQUASHFS_METADATA_SIZE << 1) + 2);
+                       if(it == NULL)
+                               MEM_ERROR();
+                       inode_table = it;
+                       inode_size += (SQUASHFS_METADATA_SIZE << 1) + 2;
+               }
+
+               c_byte = mangle(inode_table + inode_bytes + BLOCK_OFFSET,
+                       data_cache, SQUASHFS_METADATA_SIZE,
+                       SQUASHFS_METADATA_SIZE, noI, 0);
+               TRACE("Inode block @ 0x%x, size %d\n", inode_bytes, c_byte);
+               SQUASHFS_SWAP_SHORTS(&c_byte, inode_table + inode_bytes, 1);
+               inode_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) + BLOCK_OFFSET;
+               total_inode_bytes += SQUASHFS_METADATA_SIZE + BLOCK_OFFSET;
+               memmove(data_cache, data_cache + SQUASHFS_METADATA_SIZE,
+                       cache_bytes - SQUASHFS_METADATA_SIZE);
+               cache_bytes -= SQUASHFS_METADATA_SIZE;
+       }
+
+       data_space = (cache_size - cache_bytes);
+       if(data_space < req_size) {
+                       int realloc_size = cache_size == 0 ?
+                               ((req_size + SQUASHFS_METADATA_SIZE) &
+                               ~(SQUASHFS_METADATA_SIZE - 1)) : req_size -
+                               data_space;
+
+                       void *dc = realloc(data_cache, cache_size +
+                               realloc_size);
+                       if(dc == NULL)
+                               MEM_ERROR();
+                       cache_size += realloc_size;
+                       data_cache = dc;
+       }
+
+       cache_bytes += req_size;
+
+       return data_cache + cache_bytes - req_size;
+}
+
+
+int read_bytes(int fd, void *buff, int bytes)
+{
+       int res, count;
+
+       for(count = 0; count < bytes; count += res) {
+               res = read(fd, buff + count, bytes - count);
+               if(res < 1) {
+                       if(res == 0)
+                               goto bytes_read;
+                       else if(errno != EINTR) {
+                               ERROR("Read failed because %s\n",
+                                               strerror(errno));
+                               return -1;
+                       } else
+                               res = 0;
+               }
+       }
+
+bytes_read:
+       return count;
+}
+
+
+int read_fs_bytes(int fd, long long byte, int bytes, void *buff)
+{
+       off_t off = byte;
+       int res = 1;
+
+       TRACE("read_fs_bytes: reading from position 0x%llx, bytes %d\n",
+               byte, bytes);
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &pos_mutex);
+       pthread_mutex_lock(&pos_mutex);
+       if(lseek(fd, start_offset + off, SEEK_SET) == -1) {
+               ERROR("read_fs_bytes: Lseek on destination failed because %s, "
+                       "offset=0x%llx\n", strerror(errno), start_offset + off);
+               res = 0;
+       } else if(read_bytes(fd, buff, bytes) < bytes) {
+               ERROR("Read on destination failed\n");
+               res = 0;
+       }
+
+       pthread_cleanup_pop(1);
+       return res;
+}
+
+
+int write_bytes(int fd, void *buff, int bytes)
+{
+       int res, count;
+
+       for(count = 0; count < bytes; count += res) {
+               res = write(fd, buff + count, bytes - count);
+               if(res == -1) {
+                       if(errno != EINTR) {
+                               ERROR("Write failed because %s\n",
+                                               strerror(errno));
+                               return -1;
+                       }
+                       res = 0;
+               }
+       }
+
+       return 0;
+}
+
+
+void write_destination(int fd, long long byte, int bytes, void *buff)
+{
+       off_t off = byte;
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &pos_mutex);
+       pthread_mutex_lock(&pos_mutex);
+
+       if(lseek(fd, start_offset + off, SEEK_SET) == -1) {
+               ERROR("write_destination: Lseek on destination "
+                       "failed because %s, offset=0x%llx\n", strerror(errno),
+                       start_offset + off);
+               BAD_ERROR("Probably out of space on output %s\n",
+                       block_device ? "block device" : "filesystem");
+       }
+
+       if(write_bytes(fd, buff, bytes) == -1)
+               BAD_ERROR("Failed to write to output %s\n",
+                       block_device ? "block device" : "filesystem");
+
+       pthread_cleanup_pop(1);
+}
+
+
+long long write_inodes()
+{
+       unsigned short c_byte;
+       int avail_bytes;
+       char *datap = data_cache;
+       long long start_bytes = bytes;
+
+       while(cache_bytes) {
+               if(inode_size - inode_bytes <
+                               ((SQUASHFS_METADATA_SIZE << 1) + 2)) {
+                       void *it = realloc(inode_table, inode_size +
+                               ((SQUASHFS_METADATA_SIZE << 1) + 2));
+                       if(it == NULL)
+                               MEM_ERROR();
+                       inode_size += (SQUASHFS_METADATA_SIZE << 1) + 2;
+                       inode_table = it;
+               }
+               avail_bytes = cache_bytes > SQUASHFS_METADATA_SIZE ?
+                       SQUASHFS_METADATA_SIZE : cache_bytes;
+               c_byte = mangle(inode_table + inode_bytes + BLOCK_OFFSET, datap,
+                       avail_bytes, SQUASHFS_METADATA_SIZE, noI, 0);
+               TRACE("Inode block @ 0x%x, size %d\n", inode_bytes, c_byte);
+               SQUASHFS_SWAP_SHORTS(&c_byte, inode_table + inode_bytes, 1); 
+               inode_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) + BLOCK_OFFSET;
+               total_inode_bytes += avail_bytes + BLOCK_OFFSET;
+               datap += avail_bytes;
+               cache_bytes -= avail_bytes;
+       }
+
+       write_destination(fd, bytes, inode_bytes,  inode_table);
+       bytes += inode_bytes;
+
+       return start_bytes;
+}
+
+
+long long write_directories()
+{
+       unsigned short c_byte;
+       int avail_bytes;
+       char *directoryp = directory_data_cache;
+       long long start_bytes = bytes;
+
+       while(directory_cache_bytes) {
+               if(directory_size - directory_bytes <
+                               ((SQUASHFS_METADATA_SIZE << 1) + 2)) {
+                       void *dt = realloc(directory_table,
+                               directory_size + ((SQUASHFS_METADATA_SIZE << 1)
+                               + 2));
+                       if(dt == NULL)
+                               MEM_ERROR();
+                       directory_size += (SQUASHFS_METADATA_SIZE << 1) + 2;
+                       directory_table = dt;
+               }
+               avail_bytes = directory_cache_bytes > SQUASHFS_METADATA_SIZE ?
+                       SQUASHFS_METADATA_SIZE : directory_cache_bytes;
+               c_byte = mangle(directory_table + directory_bytes +
+                       BLOCK_OFFSET, directoryp, avail_bytes,
+                       SQUASHFS_METADATA_SIZE, noI, 0);
+               TRACE("Directory block @ 0x%x, size %d\n", directory_bytes,
+                       c_byte);
+               SQUASHFS_SWAP_SHORTS(&c_byte,
+                       directory_table + directory_bytes, 1);
+               directory_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) +
+                       BLOCK_OFFSET;
+               total_directory_bytes += avail_bytes + BLOCK_OFFSET;
+               directoryp += avail_bytes;
+               directory_cache_bytes -= avail_bytes;
+       }
+       write_destination(fd, bytes, directory_bytes, directory_table);
+       bytes += directory_bytes;
+
+       return start_bytes;
+}
+
+
+long long write_id_table()
+{
+       unsigned int id_bytes = SQUASHFS_ID_BYTES(id_count);
+       unsigned int p[id_count];
+       int i;
+
+       TRACE("write_id_table: ids %d, id_bytes %d\n", id_count, id_bytes);
+       for(i = 0; i < id_count; i++) {
+               TRACE("write_id_table: id index %d, id %d", i, id_table[i]->id);
+               SQUASHFS_SWAP_INTS(&id_table[i]->id, p + i, 1);
+       }
+
+       return generic_write_table(id_bytes, p, 0, NULL, noI || noId);
+}
+
+
+struct id *get_id(unsigned int id)
+{
+       int hash = ID_HASH(id);
+       struct id *entry = id_hash_table[hash];
+
+       for(; entry; entry = entry->next)
+               if(entry->id == id)
+                       break;
+
+       return entry;
+}
+
+
+struct id *create_id(unsigned int id)
+{
+       int hash = ID_HASH(id);
+       struct id *entry = malloc(sizeof(struct id));
+       if(entry == NULL)
+               MEM_ERROR();
+       entry->id = id;
+       entry->index = id_count ++;
+       entry->flags = 0;
+       entry->next = id_hash_table[hash];
+       id_hash_table[hash] = entry;
+       id_table[entry->index] = entry;
+       return entry;
+}
+
+
+unsigned int get_uid(unsigned int uid)
+{
+       struct id *entry = get_id(uid);
+
+       if(entry == NULL) {
+               if(id_count == SQUASHFS_IDS)
+                       BAD_ERROR("Out of uids!\n");
+               entry = create_id(uid);
+       }
+
+       if((entry->flags & ISA_UID) == 0) {
+               entry->flags |= ISA_UID;
+               uid_count ++;
+       }
+
+       return entry->index;
+}
+
+
+unsigned int get_guid(unsigned int guid)
+{
+       struct id *entry = get_id(guid);
+
+       if(entry == NULL) {
+               if(id_count == SQUASHFS_IDS)
+                       BAD_ERROR("Out of gids!\n");
+               entry = create_id(guid);
+       }
+
+       if((entry->flags & ISA_GID) == 0) {
+               entry->flags |= ISA_GID;
+               guid_count ++;
+       }
+
+       return entry->index;
+}
+
+
+#define ALLOC_SIZE 128
+
+char *_pathname(struct dir_ent *dir_ent, char *pathname, int *size)
+{
+       if(pathname == NULL) {
+               pathname = malloc(ALLOC_SIZE);
+               if(pathname == NULL)
+                       MEM_ERROR();
+       }
+
+       for(;;) {
+               int res = snprintf(pathname, *size, "%s/%s", 
+                       dir_ent->our_dir->pathname,
+                       dir_ent->source_name ? : dir_ent->name);
+
+               if(res < 0)
+                       BAD_ERROR("snprintf failed in pathname\n");
+               else if(res >= *size) {
+                       /*
+                        * pathname is too small to contain the result, so
+                        * increase it and try again
+                        */
+                       *size = (res + ALLOC_SIZE) & ~(ALLOC_SIZE - 1);
+                       pathname = realloc(pathname, *size);
+                       if(pathname == NULL)
+                               MEM_ERROR();
+               } else
+                       break;
+       }
+
+       return pathname;
+}
+
+
+char *pathname(struct dir_ent *dir_ent)
+{
+       static char *pathname = NULL;
+       static int size = ALLOC_SIZE;
+
+       if (dir_ent->nonstandard_pathname)
+               return dir_ent->nonstandard_pathname;
+
+       return pathname = _pathname(dir_ent, pathname, &size);
+}
+
+
+char *pathname_reader(struct dir_ent *dir_ent)
+{
+       static char *pathname = NULL;
+       static int size = ALLOC_SIZE;
+
+       if (dir_ent->nonstandard_pathname)
+               return dir_ent->nonstandard_pathname;
+
+       return pathname = _pathname(dir_ent, pathname, &size);
+}
+
+
+char *subpathname(struct dir_ent *dir_ent)
+{
+       static char *subpath = NULL;
+       static int size = ALLOC_SIZE;
+       int res;
+
+       if(subpath == NULL) {
+               subpath = malloc(ALLOC_SIZE);
+               if(subpath == NULL)
+                       MEM_ERROR();
+       }
+
+       for(;;) {
+               if(dir_ent->our_dir->subpath[0] != '\0')
+                       res = snprintf(subpath, size, "%s/%s",
+                               dir_ent->our_dir->subpath, dir_ent->name);
+               else
+                       res = snprintf(subpath, size, "/%s", dir_ent->name);
+
+               if(res < 0)
+                       BAD_ERROR("snprintf failed in subpathname\n");
+               else if(res >= size) {
+                       /*
+                        * subpath is too small to contain the result, so
+                        * increase it and try again
+                        */
+                       size = (res + ALLOC_SIZE) & ~(ALLOC_SIZE - 1);
+                       subpath = realloc(subpath, size);
+                       if(subpath == NULL)
+                               MEM_ERROR();
+               } else
+                       break;
+       }
+
+       return subpath;
+}
+
+
+static inline unsigned int get_inode_no(struct inode_info *inode)
+{
+       return inode->inode_number;
+}
+
+
+static inline unsigned int get_parent_no(struct dir_info *dir)
+{
+       return dir->depth ? get_inode_no(dir->dir_ent->inode) : inode_no;
+}
+
+       
+static inline time_t get_time(time_t time)
+{
+       if(all_time_opt) {
+               if(clamping)
+                       return time > all_time ? all_time : time;
+               else
+                       return all_time;
+       }
+
+       return time;
+}
+
+
+int create_inode(squashfs_inode *i_no, struct dir_info *dir_info,
+       struct dir_ent *dir_ent, int type, long long byte_size,
+       long long start_block, unsigned int offset, unsigned int *block_list,
+       struct fragment *fragment, struct directory *dir_in, long long sparse)
+{
+       struct stat *buf = &dir_ent->inode->buf;
+       union squashfs_inode_header inode_header;
+       struct squashfs_base_inode_header *base = &inode_header.base;
+       void *inode;
+       char *filename = pathname(dir_ent);
+       int nlink = dir_ent->inode->nlink;
+       int xattr = read_xattrs(dir_ent);
+
+       switch(type) {
+       case SQUASHFS_FILE_TYPE:
+               if(dir_ent->inode->nlink > 1 ||
+                               byte_size >= (1LL << 32) ||
+                               start_block >= (1LL << 32) ||
+                               sparse || IS_XATTR(xattr))
+                       type = SQUASHFS_LREG_TYPE;
+               break;
+       case SQUASHFS_DIR_TYPE:
+               if(dir_info->dir_is_ldir || IS_XATTR(xattr))
+                       type = SQUASHFS_LDIR_TYPE;
+               break;
+       case SQUASHFS_SYMLINK_TYPE:
+               if(IS_XATTR(xattr))
+                       type = SQUASHFS_LSYMLINK_TYPE;
+               break;
+       case SQUASHFS_BLKDEV_TYPE:
+               if(IS_XATTR(xattr))
+                       type = SQUASHFS_LBLKDEV_TYPE;
+               break;
+       case SQUASHFS_CHRDEV_TYPE:
+               if(IS_XATTR(xattr))
+                       type = SQUASHFS_LCHRDEV_TYPE;
+               break;
+       case SQUASHFS_FIFO_TYPE:
+               if(IS_XATTR(xattr))
+                       type = SQUASHFS_LFIFO_TYPE;
+               break;
+       case SQUASHFS_SOCKET_TYPE:
+               if(IS_XATTR(xattr))
+                       type = SQUASHFS_LSOCKET_TYPE;
+               break;
+       }
+                       
+       base->mode = SQUASHFS_MODE(buf->st_mode);
+       base->uid = get_uid((unsigned int) global_uid == -1 ?
+               buf->st_uid : global_uid);
+       base->inode_type = type;
+       base->guid = get_guid((unsigned int) global_gid == -1 ?
+               buf->st_gid : global_gid);
+       base->mtime = get_time(buf->st_mtime);
+       base->inode_number = get_inode_no(dir_ent->inode);
+
+       if(type == SQUASHFS_FILE_TYPE) {
+               int i;
+               struct squashfs_reg_inode_header *reg = &inode_header.reg;
+               size_t off = offsetof(struct squashfs_reg_inode_header, block_list);
+
+               inode = get_inode(sizeof(*reg) + offset * sizeof(unsigned int));
+               reg->file_size = byte_size;
+               reg->start_block = start_block;
+               reg->fragment = fragment->index;
+               reg->offset = fragment->offset;
+               SQUASHFS_SWAP_REG_INODE_HEADER(reg, inode);
+               SQUASHFS_SWAP_INTS(block_list, inode + off, offset);
+               TRACE("File inode, file_size %lld, start_block 0x%llx, blocks "
+                       "%d, fragment %d, offset %d, size %d\n", byte_size,
+                       start_block, offset, fragment->index, fragment->offset,
+                       fragment->size);
+               for(i = 0; i < offset; i++)
+                       TRACE("Block %d, size %d\n", i, block_list[i]);
+       }
+       else if(type == SQUASHFS_LREG_TYPE) {
+               int i;
+               struct squashfs_lreg_inode_header *reg = &inode_header.lreg;
+               size_t off = offsetof(struct squashfs_lreg_inode_header, block_list);
+
+               inode = get_inode(sizeof(*reg) + offset * sizeof(unsigned int));
+               reg->nlink = nlink;
+               reg->file_size = byte_size;
+               reg->start_block = start_block;
+               reg->fragment = fragment->index;
+               reg->offset = fragment->offset;
+               if(sparse && sparse >= byte_size)
+                       sparse = byte_size - 1;
+               reg->sparse = sparse;
+               reg->xattr = xattr;
+               SQUASHFS_SWAP_LREG_INODE_HEADER(reg, inode);
+               SQUASHFS_SWAP_INTS(block_list, inode + off, offset);
+               TRACE("Long file inode, file_size %lld, start_block 0x%llx, "
+                       "blocks %d, fragment %d, offset %d, size %d, nlink %d"
+                       "\n", byte_size, start_block, offset, fragment->index,
+                       fragment->offset, fragment->size, nlink);
+               for(i = 0; i < offset; i++)
+                       TRACE("Block %d, size %d\n", i, block_list[i]);
+       }
+       else if(type == SQUASHFS_LDIR_TYPE) {
+               int i;
+               unsigned char *p;
+               struct squashfs_ldir_inode_header *dir = &inode_header.ldir;
+               struct cached_dir_index *index = dir_in->index;
+               unsigned int i_count = dir_in->i_count;
+               unsigned int i_size = dir_in->i_size;
+
+               if(byte_size >= 1 << 27)
+                       BAD_ERROR("directory greater than 2^27-1 bytes!\n");
+
+               inode = get_inode(sizeof(*dir) + i_size);
+               dir->inode_type = SQUASHFS_LDIR_TYPE;
+               dir->nlink = dir_ent->dir->directory_count + 2;
+               dir->file_size = byte_size;
+               dir->offset = offset;
+               dir->start_block = start_block;
+               dir->i_count = i_count;
+               dir->parent_inode = get_parent_no(dir_ent->our_dir);
+               dir->xattr = xattr;
+
+               SQUASHFS_SWAP_LDIR_INODE_HEADER(dir, inode);
+               p = inode + offsetof(struct squashfs_ldir_inode_header, index);
+               for(i = 0; i < i_count; i++) {
+                       SQUASHFS_SWAP_DIR_INDEX(&index[i].index, p);
+                       p += offsetof(struct squashfs_dir_index, name);
+                       memcpy(p, index[i].name, index[i].index.size + 1);
+                       p += index[i].index.size + 1;
+               }
+               TRACE("Long directory inode, file_size %lld, start_block "
+                       "0x%llx, offset 0x%x, nlink %d\n", byte_size,
+                       start_block, offset, dir_ent->dir->directory_count + 2);
+       }
+       else if(type == SQUASHFS_DIR_TYPE) {
+               struct squashfs_dir_inode_header *dir = &inode_header.dir;
+
+               inode = get_inode(sizeof(*dir));
+               dir->nlink = dir_ent->dir->directory_count + 2;
+               dir->file_size = byte_size;
+               dir->offset = offset;
+               dir->start_block = start_block;
+               dir->parent_inode = get_parent_no(dir_ent->our_dir);
+               SQUASHFS_SWAP_DIR_INODE_HEADER(dir, inode);
+               TRACE("Directory inode, file_size %lld, start_block 0x%llx, "
+                       "offset 0x%x, nlink %d\n", byte_size, start_block,
+                       offset, dir_ent->dir->directory_count + 2);
+       }
+       else if(type == SQUASHFS_CHRDEV_TYPE || type == SQUASHFS_BLKDEV_TYPE) {
+               struct squashfs_dev_inode_header *dev = &inode_header.dev;
+               unsigned int major = major(buf->st_rdev);
+               unsigned int minor = minor(buf->st_rdev);
+
+               if(major > 0xfff) {
+                       ERROR("Major %d out of range in device node %s, "
+                               "truncating to %d\n", major, filename,
+                               major & 0xfff);
+                       major &= 0xfff;
+               }
+               if(minor > 0xfffff) {
+                       ERROR("Minor %d out of range in device node %s, "
+                               "truncating to %d\n", minor, filename,
+                               minor & 0xfffff);
+                       minor &= 0xfffff;
+               }
+               inode = get_inode(sizeof(*dev));
+               dev->nlink = nlink;
+               dev->rdev = (major << 8) | (minor & 0xff) |
+                               ((minor & ~0xff) << 12);
+               SQUASHFS_SWAP_DEV_INODE_HEADER(dev, inode);
+               TRACE("Device inode, rdev 0x%x, nlink %d\n", dev->rdev, nlink);
+       }
+       else if(type == SQUASHFS_LCHRDEV_TYPE || type == SQUASHFS_LBLKDEV_TYPE) {
+               struct squashfs_ldev_inode_header *dev = &inode_header.ldev;
+               unsigned int major = major(buf->st_rdev);
+               unsigned int minor = minor(buf->st_rdev);
+
+               if(major > 0xfff) {
+                       ERROR("Major %d out of range in device node %s, "
+                               "truncating to %d\n", major, filename,
+                               major & 0xfff);
+                       major &= 0xfff;
+               }
+               if(minor > 0xfffff) {
+                       ERROR("Minor %d out of range in device node %s, "
+                               "truncating to %d\n", minor, filename,
+                               minor & 0xfffff);
+                       minor &= 0xfffff;
+               }
+               inode = get_inode(sizeof(*dev));
+               dev->nlink = nlink;
+               dev->rdev = (major << 8) | (minor & 0xff) |
+                               ((minor & ~0xff) << 12);
+               dev->xattr = xattr;
+               SQUASHFS_SWAP_LDEV_INODE_HEADER(dev, inode);
+               TRACE("Device inode, rdev 0x%x, nlink %d\n", dev->rdev, nlink);
+       }
+       else if(type == SQUASHFS_SYMLINK_TYPE) {
+               struct squashfs_symlink_inode_header *symlink = &inode_header.symlink;
+               int byte = strlen(dir_ent->inode->symlink);
+               size_t off = offsetof(struct squashfs_symlink_inode_header, symlink);
+
+               inode = get_inode(sizeof(*symlink) + byte);
+               symlink->nlink = nlink;
+               symlink->symlink_size = byte;
+               SQUASHFS_SWAP_SYMLINK_INODE_HEADER(symlink, inode);
+               strncpy(inode + off, dir_ent->inode->symlink, byte);
+               TRACE("Symbolic link inode, symlink_size %d, nlink %d\n", byte,
+                       nlink);
+       }
+       else if(type == SQUASHFS_LSYMLINK_TYPE) {
+               struct squashfs_symlink_inode_header *symlink = &inode_header.symlink;
+               int byte = strlen(dir_ent->inode->symlink);
+               size_t off = offsetof(struct squashfs_symlink_inode_header, symlink);
+
+               inode = get_inode(sizeof(*symlink) + byte +
+                                               sizeof(unsigned int));
+               symlink->nlink = nlink;
+               symlink->symlink_size = byte;
+               SQUASHFS_SWAP_SYMLINK_INODE_HEADER(symlink, inode);
+               strncpy(inode + off, dir_ent->inode->symlink, byte);
+               SQUASHFS_SWAP_INTS(&xattr, inode + off + byte, 1);
+               TRACE("Symbolic link inode, symlink_size %d, nlink %d\n", byte,
+                       nlink);
+       }
+       else if(type == SQUASHFS_FIFO_TYPE || type == SQUASHFS_SOCKET_TYPE) {
+               struct squashfs_ipc_inode_header *ipc = &inode_header.ipc;
+
+               inode = get_inode(sizeof(*ipc));
+               ipc->nlink = nlink;
+               SQUASHFS_SWAP_IPC_INODE_HEADER(ipc, inode);
+               TRACE("ipc inode, type %s, nlink %d\n", type ==
+                       SQUASHFS_FIFO_TYPE ? "fifo" : "socket", nlink);
+       }
+       else if(type == SQUASHFS_LFIFO_TYPE || type == SQUASHFS_LSOCKET_TYPE) {
+               struct squashfs_lipc_inode_header *ipc = &inode_header.lipc;
+
+               inode = get_inode(sizeof(*ipc));
+               ipc->nlink = nlink;
+               ipc->xattr = xattr;
+               SQUASHFS_SWAP_LIPC_INODE_HEADER(ipc, inode);
+               TRACE("ipc inode, type %s, nlink %d\n", type ==
+                       SQUASHFS_FIFO_TYPE ? "fifo" : "socket", nlink);
+       } else
+               BAD_ERROR("Unrecognised inode %d in create_inode\n", type);
+
+       *i_no = MKINODE(inode);
+       inode_count ++;
+
+       TRACE("Created inode 0x%llx, type %d, uid %d, guid %d\n", *i_no, type,
+               base->uid, base->guid);
+
+       return TRUE;
+}
+
+
+void add_dir(squashfs_inode inode, unsigned int inode_number, char *name,
+       int type, struct directory *dir)
+{
+       unsigned char *buff;
+       struct squashfs_dir_entry idir;
+       unsigned int start_block = inode >> 16;
+       unsigned int offset = inode & 0xffff;
+       unsigned int size = strlen(name);
+       size_t name_off = offsetof(struct squashfs_dir_entry, name);
+
+       if(size > SQUASHFS_NAME_LEN) {
+               size = SQUASHFS_NAME_LEN;
+               ERROR("Filename is greater than %d characters, truncating! ..."
+                       "\n", SQUASHFS_NAME_LEN);
+       }
+
+       if(dir->p + sizeof(struct squashfs_dir_entry) + size +
+                       sizeof(struct squashfs_dir_header)
+                       >= dir->buff + dir->size) {
+               buff = realloc(dir->buff, dir->size += SQUASHFS_METADATA_SIZE);
+               if(buff == NULL)
+                       MEM_ERROR();
+
+               dir->p = (dir->p - dir->buff) + buff;
+               if(dir->entry_count_p) 
+                       dir->entry_count_p = (dir->entry_count_p - dir->buff +
+                       buff);
+               dir->index_count_p = dir->index_count_p - dir->buff + buff;
+               dir->buff = buff;
+       }
+
+       if(dir->entry_count == 256 || start_block != dir->start_block ||
+                       ((dir->entry_count_p != NULL) &&
+                       ((dir->p + sizeof(struct squashfs_dir_entry) + size -
+                       dir->index_count_p) > SQUASHFS_METADATA_SIZE)) ||
+                       ((long long) inode_number - dir->inode_number) > 32767
+                       || ((long long) inode_number - dir->inode_number)
+                       < -32768) {
+               if(dir->entry_count_p) {
+                       struct squashfs_dir_header dir_header;
+
+                       if((dir->p + sizeof(struct squashfs_dir_entry) + size -
+                                       dir->index_count_p) >
+                                       SQUASHFS_METADATA_SIZE) {
+                               if(dir->i_count % I_COUNT_SIZE == 0) {
+                                       dir->index = realloc(dir->index,
+                                               (dir->i_count + I_COUNT_SIZE) *
+                                               sizeof(struct cached_dir_index));
+                                       if(dir->index == NULL)
+                                               MEM_ERROR();
+                               }
+                               dir->index[dir->i_count].index.index =
+                                       dir->p - dir->buff;
+                               dir->index[dir->i_count].index.size = size - 1;
+                               dir->index[dir->i_count++].name = name;
+                               dir->i_size += sizeof(struct squashfs_dir_index)
+                                       + size;
+                               dir->index_count_p = dir->p;
+                       }
+
+                       dir_header.count = dir->entry_count - 1;
+                       dir_header.start_block = dir->start_block;
+                       dir_header.inode_number = dir->inode_number;
+                       SQUASHFS_SWAP_DIR_HEADER(&dir_header,
+                               dir->entry_count_p);
+
+               }
+
+
+               dir->entry_count_p = dir->p;
+               dir->start_block = start_block;
+               dir->entry_count = 0;
+               dir->inode_number = inode_number;
+               dir->p += sizeof(struct squashfs_dir_header);
+       }
+
+       idir.offset = offset;
+       idir.type = type;
+       idir.size = size - 1;
+       idir.inode_number = ((long long) inode_number - dir->inode_number);
+       SQUASHFS_SWAP_DIR_ENTRY(&idir, dir->p);
+       strncpy((char *) dir->p + name_off, name, size);
+       dir->p += sizeof(struct squashfs_dir_entry) + size;
+       dir->entry_count ++;
+}
+
+
+void write_dir(squashfs_inode *inode, struct dir_info *dir_info,
+       struct directory *dir)
+{
+       unsigned int dir_size = dir->p - dir->buff;
+       int data_space = directory_cache_size - directory_cache_bytes;
+       unsigned int directory_block, directory_offset, i_count, index;
+       unsigned short c_byte;
+
+       if(data_space < dir_size) {
+               int realloc_size = directory_cache_size == 0 ?
+                       ((dir_size + SQUASHFS_METADATA_SIZE) &
+                       ~(SQUASHFS_METADATA_SIZE - 1)) : dir_size - data_space;
+
+               void *dc = realloc(directory_data_cache,
+                       directory_cache_size + realloc_size);
+               if(dc == NULL)
+                       MEM_ERROR();
+               directory_cache_size += realloc_size;
+               directory_data_cache = dc;
+       }
+
+       if(dir_size) {
+               struct squashfs_dir_header dir_header;
+
+               dir_header.count = dir->entry_count - 1;
+               dir_header.start_block = dir->start_block;
+               dir_header.inode_number = dir->inode_number;
+               SQUASHFS_SWAP_DIR_HEADER(&dir_header, dir->entry_count_p);
+               memcpy(directory_data_cache + directory_cache_bytes, dir->buff,
+                       dir_size);
+       }
+       directory_offset = directory_cache_bytes;
+       directory_block = directory_bytes;
+       directory_cache_bytes += dir_size;
+       i_count = 0;
+       index = SQUASHFS_METADATA_SIZE - directory_offset;
+
+       while(1) {
+               while(i_count < dir->i_count &&
+                               dir->index[i_count].index.index < index)
+                       dir->index[i_count++].index.start_block =
+                               directory_bytes;
+               index += SQUASHFS_METADATA_SIZE;
+
+               if(directory_cache_bytes < SQUASHFS_METADATA_SIZE)
+                       break;
+
+               if((directory_size - directory_bytes) <
+                                       ((SQUASHFS_METADATA_SIZE << 1) + 2)) {
+                       void *dt = realloc(directory_table,
+                               directory_size + (SQUASHFS_METADATA_SIZE << 1)
+                               + 2);
+                       if(dt == NULL)
+                               MEM_ERROR();
+                       directory_size += SQUASHFS_METADATA_SIZE << 1;
+                       directory_table = dt;
+               }
+
+               c_byte = mangle(directory_table + directory_bytes +
+                               BLOCK_OFFSET, directory_data_cache,
+                               SQUASHFS_METADATA_SIZE, SQUASHFS_METADATA_SIZE,
+                               noI, 0);
+               TRACE("Directory block @ 0x%x, size %d\n", directory_bytes,
+                       c_byte);
+               SQUASHFS_SWAP_SHORTS(&c_byte,
+                       directory_table + directory_bytes, 1);
+               directory_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) +
+                       BLOCK_OFFSET;
+               total_directory_bytes += SQUASHFS_METADATA_SIZE + BLOCK_OFFSET;
+               memmove(directory_data_cache, directory_data_cache +
+                       SQUASHFS_METADATA_SIZE, directory_cache_bytes -
+                       SQUASHFS_METADATA_SIZE);
+               directory_cache_bytes -= SQUASHFS_METADATA_SIZE;
+       }
+
+       create_inode(inode, dir_info, dir_info->dir_ent, SQUASHFS_DIR_TYPE,
+               dir_size + 3, directory_block, directory_offset, NULL, NULL,
+               dir, 0);
+
+#ifdef SQUASHFS_TRACE
+       {
+               unsigned char *dirp;
+               int count;
+
+               TRACE("Directory contents of inode 0x%llx\n", *inode);
+               dirp = dir->buff;
+               while(dirp < dir->p) {
+                       char buffer[SQUASHFS_NAME_LEN + 1];
+                       struct squashfs_dir_entry idir, *idirp;
+                       struct squashfs_dir_header dirh;
+                       SQUASHFS_SWAP_DIR_HEADER((struct squashfs_dir_header *) dirp,
+                               &dirh);
+                       count = dirh.count + 1;
+                       dirp += sizeof(struct squashfs_dir_header);
+
+                       TRACE("\tStart block 0x%x, count %d\n",
+                               dirh.start_block, count);
+
+                       while(count--) {
+                               idirp = (struct squashfs_dir_entry *) dirp;
+                               SQUASHFS_SWAP_DIR_ENTRY(idirp, &idir);
+                               strncpy(buffer, idirp->name, idir.size + 1);
+                               buffer[idir.size + 1] = '\0';
+                               TRACE("\t\tname %s, inode offset 0x%x, type "
+                                       "%d\n", buffer, idir.offset, idir.type);
+                               dirp += sizeof(struct squashfs_dir_entry) + idir.size +
+                                       1;
+                       }
+               }
+       }
+#endif
+       dir_count ++;
+}
+
+
+static struct file_buffer *get_fragment(struct fragment *fragment)
+{
+       struct squashfs_fragment_entry *disk_fragment;
+       struct file_buffer *buffer, *compressed_buffer;
+       long long start_block;
+       int res, size, index = fragment->index;
+       char locked;
+
+       /*
+        * Lookup fragment block in cache.
+        * If the fragment block doesn't exist, then get the compressed version
+        * from the writer cache or off disk, and decompress it.
+        *
+        * This routine has two things which complicate the code:
+        *
+        *      1. Multiple threads can simultaneously lookup/create the
+        *         same buffer.  This means a buffer needs to be "locked"
+        *         when it is being filled in, to prevent other threads from
+        *         using it when it is not ready.  This is because we now do
+        *         fragment duplicate checking in parallel.
+        *      2. We have two caches which need to be checked for the
+        *         presence of fragment blocks: the normal fragment cache
+        *         and a "reserve" cache.  The reserve cache is used to
+        *         prevent an unnecessary pipeline stall when the fragment cache
+        *         is full of fragments waiting to be compressed.
+        */
+
+       if(fragment->index == SQUASHFS_INVALID_FRAG)
+               return NULL;
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex);
+       pthread_mutex_lock(&dup_mutex);
+
+again:
+       buffer = cache_lookup_nowait(fragment_buffer, index, &locked);
+       if(buffer) {
+               pthread_mutex_unlock(&dup_mutex);
+               if(locked)
+                       /* got a buffer being filled in.  Wait for it */
+                       cache_wait_unlock(buffer);
+               goto finished;
+       }
+
+       /* not in fragment cache, is it in the reserve cache? */
+       buffer = cache_lookup_nowait(reserve_cache, index, &locked);
+       if(buffer) {
+               pthread_mutex_unlock(&dup_mutex);
+               if(locked)
+                       /* got a buffer being filled in.  Wait for it */
+                       cache_wait_unlock(buffer);
+               goto finished;
+       }
+
+       /* in neither cache, try to get it from the fragment cache */
+       buffer = cache_get_nowait(fragment_buffer, index);
+       if(!buffer) {
+               /*
+                * no room, get it from the reserve cache, this is
+                * dimensioned so it will always have space (no more than
+                * processors + 1 can have an outstanding reserve buffer)
+                */
+               buffer = cache_get_nowait(reserve_cache, index);
+               if(!buffer) {
+                       /* failsafe */
+                       ERROR("no space in reserve cache\n");
+                       goto again;
+               }
+       }
+
+       pthread_mutex_unlock(&dup_mutex);
+
+       compressed_buffer = cache_lookup(fwriter_buffer, index);
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex);
+       pthread_mutex_lock(&fragment_mutex);
+       disk_fragment = &fragment_table[index];
+       size = SQUASHFS_COMPRESSED_SIZE_BLOCK(disk_fragment->size);
+       start_block = disk_fragment->start_block;
+       pthread_cleanup_pop(1);
+
+       if(SQUASHFS_COMPRESSED_BLOCK(disk_fragment->size)) {
+               int error;
+               char *data;
+
+               if(compressed_buffer)
+                       data = compressed_buffer->data;
+               else {
+                       data = read_from_disk(start_block, size);
+                       if(data == NULL) {
+                               ERROR("Failed to read fragment from output"
+                                       " filesystem\n");
+                               BAD_ERROR("Output filesystem corrupted?\n");
+                       }
+               }
+
+               res = compressor_uncompress(comp, buffer->data, data, size,
+                       block_size, &error);
+               if(res == -1)
+                       BAD_ERROR("%s uncompress failed with error code %d\n",
+                               comp->name, error);
+       } else if(compressed_buffer)
+               memcpy(buffer->data, compressed_buffer->data, size);
+       else {
+               res = read_fs_bytes(fd, start_block, size, buffer->data);
+               if(res == 0) {
+                       ERROR("Failed to read fragment from output "
+                               "filesystem\n");
+                       BAD_ERROR("Output filesystem corrupted?\n");
+               }
+       }
+
+       cache_unlock(buffer);
+       cache_block_put(compressed_buffer);
+
+finished:
+       pthread_cleanup_pop(0);
+
+       return buffer;
+}
+
+
+unsigned short get_fragment_checksum(struct file_info *file)
+{
+       struct file_buffer *frag_buffer;
+       struct append_file *append;
+       int res, index = file->fragment->index;
+       unsigned short checksum;
+
+       if(index == SQUASHFS_INVALID_FRAG)
+               return 0;
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex);
+       pthread_mutex_lock(&dup_mutex);
+       res = file->have_frag_checksum;
+       checksum = file->fragment_checksum;
+       pthread_cleanup_pop(1);
+
+       if(res)
+               return checksum;
+
+       frag_buffer = get_fragment(file->fragment);
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex);
+
+       for(append = file_mapping[index]; append; append = append->next) {
+               int offset = append->file->fragment->offset;
+               int size = append->file->fragment->size;
+               unsigned short cksum =
+                       get_checksum_mem(frag_buffer->data + offset, size);
+
+               if(file == append->file)
+                       checksum = cksum;
+
+               pthread_mutex_lock(&dup_mutex);
+               append->file->fragment_checksum = cksum;
+               append->file->have_frag_checksum = TRUE;
+               pthread_mutex_unlock(&dup_mutex);
+       }
+
+       cache_block_put(frag_buffer);
+       pthread_cleanup_pop(0);
+
+       return checksum;
+}
+
+
+void ensure_fragments_flushed()
+{
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex);
+       pthread_mutex_lock(&fragment_mutex);
+
+       while(fragments_outstanding)
+               pthread_cond_wait(&fragment_waiting, &fragment_mutex);
+
+       pthread_cleanup_pop(1);
+}
+
+
+void lock_fragments()
+{
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex);
+       pthread_mutex_lock(&fragment_mutex);
+       fragments_locked = TRUE;
+       pthread_cleanup_pop(1);
+}
+
+
+void log_fragment(unsigned int fragment, long long start)
+{
+       if(logging)
+               fprintf(log_fd, "Fragment %u, %lld\n", fragment, start);
+}
+
+
+void unlock_fragments()
+{
+       int frg, size;
+       struct file_buffer *write_buffer;
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex);
+       pthread_mutex_lock(&fragment_mutex);
+
+       /*
+        * Note queue_empty() is inherently racy with respect to concurrent
+        * queue get and pushes.  We avoid this because we're holding the
+        * fragment_mutex which ensures no other threads can be using the
+        * queue at this time.
+        */
+       while(!queue_empty(locked_fragment)) {
+               write_buffer = queue_get(locked_fragment);
+               frg = write_buffer->block;      
+               size = SQUASHFS_COMPRESSED_SIZE_BLOCK(fragment_table[frg].size);
+               fragment_table[frg].start_block = bytes;
+               write_buffer->block = bytes;
+               bytes += size;
+               fragments_outstanding --;
+               queue_put(to_writer, write_buffer);
+               log_fragment(frg, fragment_table[frg].start_block);
+               TRACE("fragment_locked writing fragment %d, compressed size %d"
+                       "\n", frg, size);
+       }
+       fragments_locked = FALSE;
+       pthread_cleanup_pop(1);
+}
+
+/* Called with the fragment_mutex locked */
+void add_pending_fragment(struct file_buffer *write_buffer, int c_byte,
+       int fragment)
+{
+       fragment_table[fragment].size = c_byte;
+       write_buffer->block = fragment;
+
+       queue_put(locked_fragment, write_buffer);
+}
+
+
+void write_fragment(struct file_buffer *fragment)
+{
+       static long long sequence = 0;
+
+       if(fragment == NULL)
+               return;
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex);
+       pthread_mutex_lock(&fragment_mutex);
+       fragment_table[fragment->block].unused = 0;
+       fragment->sequence = sequence ++;
+       fragments_outstanding ++;
+       queue_put(to_frag, fragment);
+       pthread_cleanup_pop(1);
+}
+
+
+struct file_buffer *allocate_fragment()
+{
+       struct file_buffer *fragment = cache_get(fragment_buffer, fragments);
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex);
+       pthread_mutex_lock(&fragment_mutex);
+
+       if(fragments % FRAG_SIZE == 0) {
+               void *ft = realloc(fragment_table, (fragments +
+                       FRAG_SIZE) * sizeof(struct squashfs_fragment_entry));
+               if(ft == NULL)
+                       MEM_ERROR();
+               fragment_table = ft;
+       }
+
+       fragment->size = 0;
+       fragment->block = fragments ++;
+
+       pthread_cleanup_pop(1);
+
+       return fragment;
+}
+
+
+static struct fragment empty_fragment = {SQUASHFS_INVALID_FRAG, 0, 0};
+
+
+void free_fragment(struct fragment *fragment)
+{
+       if(fragment != &empty_fragment)
+               free(fragment);
+}
+
+
+struct fragment *get_and_fill_fragment(struct file_buffer *file_buffer,
+       struct dir_ent *dir_ent)
+{
+       struct fragment *ffrg;
+       struct file_buffer **fragment;
+
+       if(file_buffer == NULL || file_buffer->size == 0)
+               return &empty_fragment;
+
+       fragment = eval_frag_actions(root_dir, dir_ent);
+
+       if((*fragment) && (*fragment)->size + file_buffer->size > block_size) {
+               write_fragment(*fragment);
+               *fragment = NULL;
+       }
+
+       ffrg = malloc(sizeof(struct fragment));
+       if(ffrg == NULL)
+               MEM_ERROR();
+
+       if(*fragment == NULL)
+               *fragment = allocate_fragment();
+
+       ffrg->index = (*fragment)->block;
+       ffrg->offset = (*fragment)->size;
+       ffrg->size = file_buffer->size;
+       memcpy((*fragment)->data + (*fragment)->size, file_buffer->data,
+               file_buffer->size);
+       (*fragment)->size += file_buffer->size;
+
+       return ffrg;
+}
+
+
+long long generic_write_table(int length, void *buffer, int length2,
+       void *buffer2, int uncompressed)
+{
+       int meta_blocks = (length + SQUASHFS_METADATA_SIZE - 1) /
+               SQUASHFS_METADATA_SIZE;
+       long long *list, start_bytes;
+       int compressed_size, i, list_size = meta_blocks * sizeof(long long);
+       unsigned short c_byte;
+       char cbuffer[(SQUASHFS_METADATA_SIZE << 2) + 2];
+       
+#ifdef SQUASHFS_TRACE
+       long long obytes = bytes;
+       int olength = length;
+#endif
+
+       list = malloc(list_size);
+       if(list == NULL)
+               MEM_ERROR();
+
+       for(i = 0; i < meta_blocks; i++) {
+               int avail_bytes = length > SQUASHFS_METADATA_SIZE ?
+                       SQUASHFS_METADATA_SIZE : length;
+               c_byte = mangle(cbuffer + BLOCK_OFFSET, buffer + i *
+                       SQUASHFS_METADATA_SIZE , avail_bytes,
+                       SQUASHFS_METADATA_SIZE, uncompressed, 0);
+               SQUASHFS_SWAP_SHORTS(&c_byte, cbuffer, 1);
+               list[i] = bytes;
+               compressed_size = SQUASHFS_COMPRESSED_SIZE(c_byte) +
+                       BLOCK_OFFSET;
+               TRACE("block %d @ 0x%llx, compressed size %d\n", i, bytes,
+                       compressed_size);
+               write_destination(fd, bytes, compressed_size, cbuffer);
+               bytes += compressed_size;
+               total_bytes += avail_bytes;
+               length -= avail_bytes;
+       }
+
+       start_bytes = bytes;
+       if(length2) {
+               write_destination(fd, bytes, length2, buffer2);
+               bytes += length2;
+               total_bytes += length2;
+       }
+               
+       SQUASHFS_INSWAP_LONG_LONGS(list, meta_blocks);
+       write_destination(fd, bytes, list_size, list);
+       bytes += list_size;
+       total_bytes += list_size;
+
+       TRACE("generic_write_table: total uncompressed %d compressed %lld\n",
+               olength, bytes - obytes);
+
+       free(list);
+
+       return start_bytes;
+}
+
+
+long long write_fragment_table()
+{
+       unsigned int frag_bytes = SQUASHFS_FRAGMENT_BYTES(fragments);
+       int i;
+
+       TRACE("write_fragment_table: fragments %d, frag_bytes %d\n", fragments,
+               frag_bytes);
+       for(i = 0; i < fragments; i++) {
+               TRACE("write_fragment_table: fragment %d, start_block 0x%llx, "
+                       "size %d\n", i, fragment_table[i].start_block,
+                       fragment_table[i].size);
+               SQUASHFS_INSWAP_FRAGMENT_ENTRY(&fragment_table[i]);
+       }
+
+       return generic_write_table(frag_bytes, fragment_table, 0, NULL, noF);
+}
+
+
+char read_from_file_buffer[SQUASHFS_FILE_MAX_SIZE];
+static char *read_from_disk(long long start, unsigned int avail_bytes)
+{
+       int res;
+
+       res = read_fs_bytes(fd, start, avail_bytes, read_from_file_buffer);
+       if(res == 0)
+               return NULL;
+
+       return read_from_file_buffer;
+}
+
+
+char read_from_file_buffer2[SQUASHFS_FILE_MAX_SIZE];
+char *read_from_disk2(long long start, unsigned int avail_bytes)
+{
+       int res;
+
+       res = read_fs_bytes(fd, start, avail_bytes, read_from_file_buffer2);
+       if(res == 0)
+               return NULL;
+
+       return read_from_file_buffer2;
+}
+
+
+/*
+ * Compute 16 bit BSD checksum over the data
+ */
+unsigned short get_checksum(char *buff, int bytes, unsigned short chksum)
+{
+       unsigned char *b = (unsigned char *) buff;
+
+       while(bytes --) {
+               chksum = (chksum & 1) ? (chksum >> 1) | 0x8000 : chksum >> 1;
+               chksum += *b++;
+       }
+
+       return chksum;
+}
+
+
+unsigned short get_checksum_disk(long long start, long long l,
+       unsigned int *blocks)
+{
+       unsigned short chksum = 0;
+       unsigned int bytes;
+       struct file_buffer *write_buffer;
+       int i;
+
+       for(i = 0; l; i++)  {
+               bytes = SQUASHFS_COMPRESSED_SIZE_BLOCK(blocks[i]);
+               if(bytes == 0) /* sparse block */
+                       continue;
+               write_buffer = cache_lookup(bwriter_buffer, start);
+               if(write_buffer) {
+                       chksum = get_checksum(write_buffer->data, bytes,
+                               chksum);
+                       cache_block_put(write_buffer);
+               } else {
+                       void *data = read_from_disk(start, bytes);
+                       if(data == NULL) {      
+                               ERROR("Failed to checksum data from output"
+                                       " filesystem\n");
+                               BAD_ERROR("Output filesystem corrupted?\n");
+                       }
+
+                       chksum = get_checksum(data, bytes, chksum);
+               }
+
+               l -= bytes;
+               start += bytes;
+       }
+
+       return chksum;
+}
+
+
+unsigned short get_checksum_mem(char *buff, int bytes)
+{
+       return get_checksum(buff, bytes, 0);
+}
+
+
+unsigned short get_checksum_mem_buffer(struct file_buffer *file_buffer)
+{
+       if(file_buffer == NULL)
+               return 0;
+       else
+               return get_checksum(file_buffer->data, file_buffer->size, 0);
+}
+
+
+#define DUP_HASH(a) (a & 0xffff)
+void add_file(long long start, long long file_size, long long file_bytes,
+       unsigned int *block_listp, int blocks, unsigned int fragment,
+       int offset, int bytes)
+{
+       struct fragment *frg;
+       unsigned int *block_list = block_listp;
+       struct file_info *dupl_ptr = dupl[DUP_HASH(file_size)];
+       struct append_file *append_file;
+       struct file_info *file;
+
+       if(!duplicate_checking || file_size == 0)
+               return;
+
+       for(; dupl_ptr; dupl_ptr = dupl_ptr->next) {
+               if(file_size != dupl_ptr->file_size)
+                       continue;
+               if(blocks != 0 && start != dupl_ptr->start)
+                       continue;
+               if(fragment != dupl_ptr->fragment->index)
+                       continue;
+               if(fragment != SQUASHFS_INVALID_FRAG && (offset !=
+                               dupl_ptr->fragment->offset || bytes !=
+                               dupl_ptr->fragment->size))
+                       continue;
+               return;
+       }
+
+       frg = malloc(sizeof(struct fragment));
+       if(frg == NULL)
+               MEM_ERROR();
+
+       frg->index = fragment;
+       frg->offset = offset;
+       frg->size = bytes;
+
+       file = add_non_dup(file_size, file_bytes, block_list, start, frg, 0, 0,
+               FALSE, FALSE);
+
+       if(fragment == SQUASHFS_INVALID_FRAG)
+               return;
+
+       append_file = malloc(sizeof(struct append_file));
+       if(append_file == NULL)
+               MEM_ERROR();
+
+       append_file->file = file;
+       append_file->next = file_mapping[fragment];
+       file_mapping[fragment] = append_file;
+}
+
+
+int pre_duplicate(long long file_size)
+{
+       struct file_info *dupl_ptr = dupl[DUP_HASH(file_size)];
+
+       for(; dupl_ptr; dupl_ptr = dupl_ptr->next)
+               if(dupl_ptr->file_size == file_size)
+                       return TRUE;
+
+       return FALSE;
+}
+
+
+struct file_info *add_non_dup(long long file_size, long long bytes,
+       unsigned int *block_list, long long start, struct fragment *fragment,
+       unsigned short checksum, unsigned short fragment_checksum,
+       int checksum_flag, int checksum_frag_flag)
+{
+       struct file_info *dupl_ptr = malloc(sizeof(struct file_info));
+
+       if(dupl_ptr == NULL)
+               MEM_ERROR();
+
+       dupl_ptr->file_size = file_size;
+       dupl_ptr->bytes = bytes;
+       dupl_ptr->block_list = block_list;
+       dupl_ptr->start = start;
+       dupl_ptr->fragment = fragment;
+       dupl_ptr->checksum = checksum;
+       dupl_ptr->fragment_checksum = fragment_checksum;
+       dupl_ptr->have_frag_checksum = checksum_frag_flag;
+       dupl_ptr->have_checksum = checksum_flag;
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex);
+        pthread_mutex_lock(&dup_mutex);
+       dupl_ptr->next = dupl[DUP_HASH(file_size)];
+       dupl[DUP_HASH(file_size)] = dupl_ptr;
+       dup_files ++;
+       pthread_cleanup_pop(1);
+
+       return dupl_ptr;
+}
+
+
+struct fragment *frag_duplicate(struct file_buffer *file_buffer, char *dont_put)
+{
+       struct file_info *dupl_ptr;
+       struct file_buffer *buffer;
+       struct file_info *dupl_start = file_buffer->dupl_start;
+       long long file_size = file_buffer->file_size;
+       unsigned short checksum = file_buffer->checksum;
+       int res;
+
+       if(file_buffer->duplicate) {
+               TRACE("Found duplicate file, fragment %d, size %d, offset %d, "
+                       "checksum 0x%x\n", dupl_start->fragment->index,
+                       file_size, dupl_start->fragment->offset, checksum);
+               *dont_put = TRUE;
+               return dupl_start->fragment;
+       } else {
+               *dont_put = FALSE;
+               dupl_ptr = dupl[DUP_HASH(file_size)];
+       }
+
+       for(; dupl_ptr && dupl_ptr != dupl_start; dupl_ptr = dupl_ptr->next) {
+               if(file_size == dupl_ptr->file_size && file_size ==
+                               dupl_ptr->fragment->size) {
+                       if(get_fragment_checksum(dupl_ptr) == checksum) {
+                               buffer = get_fragment(dupl_ptr->fragment);
+                               res = memcmp(file_buffer->data, buffer->data +
+                                       dupl_ptr->fragment->offset, file_size);
+                               cache_block_put(buffer);
+                               if(res == 0)
+                                       break;
+                       }
+               }
+       }
+
+       if(!dupl_ptr || dupl_ptr == dupl_start)
+               return NULL;
+
+       TRACE("Found duplicate file, fragment %d, size %d, offset %d, "
+               "checksum 0x%x\n", dupl_ptr->fragment->index, file_size,
+               dupl_ptr->fragment->offset, checksum);
+
+       return dupl_ptr->fragment;
+}
+
+
+struct file_info *duplicate(long long file_size, long long bytes,
+       unsigned int **block_list, long long *start, struct fragment **fragment,
+       struct file_buffer *file_buffer, int blocks, unsigned short checksum,
+       int checksum_flag)
+{
+       struct file_info *dupl_ptr = dupl[DUP_HASH(file_size)];
+       int frag_bytes = file_buffer ? file_buffer->size : 0;
+       unsigned short fragment_checksum = file_buffer ?
+               file_buffer->checksum : 0;
+
+       for(; dupl_ptr; dupl_ptr = dupl_ptr->next)
+               if(file_size == dupl_ptr->file_size && bytes == dupl_ptr->bytes
+                                && frag_bytes == dupl_ptr->fragment->size) {
+                       long long target_start, dup_start = dupl_ptr->start;
+                       int block;
+
+                       if(memcmp(*block_list, dupl_ptr->block_list, blocks *
+                                       sizeof(unsigned int)) != 0)
+                               continue;
+
+                       if(checksum_flag == FALSE) {
+                               checksum = get_checksum_disk(*start, bytes,
+                                       *block_list);
+                               checksum_flag = TRUE;
+                       }
+
+                       if(!dupl_ptr->have_checksum) {
+                               dupl_ptr->checksum =
+                                       get_checksum_disk(dupl_ptr->start,
+                                       dupl_ptr->bytes, dupl_ptr->block_list);
+                               dupl_ptr->have_checksum = TRUE;
+                       }
+
+                       if(checksum != dupl_ptr->checksum ||
+                                       fragment_checksum !=
+                                       get_fragment_checksum(dupl_ptr))
+                               continue;
+
+                       target_start = *start;
+                       for(block = 0; block < blocks; block ++) {
+                               int size = SQUASHFS_COMPRESSED_SIZE_BLOCK
+                                       ((*block_list)[block]);
+                               struct file_buffer *target_buffer = NULL;
+                               struct file_buffer *dup_buffer = NULL;
+                               char *target_data, *dup_data;
+                               int res;
+
+                               if(size == 0)
+                                       continue;
+                               target_buffer = cache_lookup(bwriter_buffer,
+                                       target_start);
+                               if(target_buffer)
+                                       target_data = target_buffer->data;
+                               else {
+                                       target_data =
+                                               read_from_disk(target_start,
+                                               size);
+                                       if(target_data == NULL) {
+                                               ERROR("Failed to read data from"
+                                                       " output filesystem\n");
+                                               BAD_ERROR("Output filesystem"
+                                                       " corrupted?\n");
+                                       }
+                               }
+
+                               dup_buffer = cache_lookup(bwriter_buffer,
+                                       dup_start);
+                               if(dup_buffer)
+                                       dup_data = dup_buffer->data;
+                               else {
+                                       dup_data = read_from_disk2(dup_start,
+                                               size);
+                                       if(dup_data == NULL) {
+                                               ERROR("Failed to read data from"
+                                                       " output filesystem\n");
+                                               BAD_ERROR("Output filesystem"
+                                                       " corrupted?\n");
+                                       }
+                               }
+
+                               res = memcmp(target_data, dup_data, size);
+                               cache_block_put(target_buffer);
+                               cache_block_put(dup_buffer);
+                               if(res != 0)
+                                       break;
+                               target_start += size;
+                               dup_start += size;
+                       }
+                       if(block == blocks) {
+                               struct file_buffer *frag_buffer =
+                                       get_fragment(dupl_ptr->fragment);
+
+                               if(frag_bytes == 0 ||
+                                               memcmp(file_buffer->data,
+                                               frag_buffer->data +
+                                               dupl_ptr->fragment->offset,
+                                               frag_bytes) == 0) {
+                                       TRACE("Found duplicate file, start "
+                                               "0x%llx, size %lld, checksum "
+                                               "0x%x, fragment %d, size %d, "
+                                               "offset %d, checksum 0x%x\n",
+                                               dupl_ptr->start,
+                                               dupl_ptr->bytes,
+                                               dupl_ptr->checksum,
+                                               dupl_ptr->fragment->index,
+                                               frag_bytes,
+                                               dupl_ptr->fragment->offset,
+                                               fragment_checksum);
+                                       *block_list = dupl_ptr->block_list;
+                                       *start = dupl_ptr->start;
+                                       *fragment = dupl_ptr->fragment;
+                                       cache_block_put(frag_buffer);
+                                       return 0;
+                               }
+                               cache_block_put(frag_buffer);
+                       }
+               }
+
+
+       return add_non_dup(file_size, bytes, *block_list, *start, *fragment,
+               checksum, fragment_checksum, checksum_flag, TRUE);
+}
+
+
+static inline int is_fragment(struct inode_info *inode)
+{
+       off_t file_size = inode->buf.st_size;
+
+       /*
+        * If this block is to be compressed differently to the
+        * fragment compression then it cannot be a fragment
+        */
+       if(inode->noF != noF)
+               return FALSE;
+
+       return !inode->no_fragments && file_size && (file_size < block_size ||
+               (inode->always_use_fragments && file_size & (block_size - 1)));
+}
+
+
+void put_file_buffer(struct file_buffer *file_buffer)
+{
+       /*
+        * Decide where to send the file buffer:
+        * - compressible non-fragment blocks go to the deflate threads,
+        * - fragments go to the process fragment threads,
+        * - all others go directly to the main thread
+        */
+       if(file_buffer->error) {
+               file_buffer->fragment = 0;
+               seq_queue_put(to_main, file_buffer);
+       } else if (file_buffer->file_size == 0)
+               seq_queue_put(to_main, file_buffer);
+       else if(file_buffer->fragment)
+               queue_put(to_process_frag, file_buffer);
+       else
+               queue_put(to_deflate, file_buffer);
+}
+
+
+static int seq = 0;
+void reader_read_process(struct dir_ent *dir_ent)
+{
+       long long bytes = 0;
+       struct inode_info *inode = dir_ent->inode;
+       struct file_buffer *prev_buffer = NULL, *file_buffer;
+       int status, byte, res, child;
+       int file = pseudo_exec_file(get_pseudo_file(inode->pseudo_id), &child);
+
+       if(!file) {
+               file_buffer = cache_get_nohash(reader_buffer);
+               file_buffer->sequence = seq ++;
+               goto read_err;
+       }
+
+       while(1) {
+               file_buffer = cache_get_nohash(reader_buffer);
+               file_buffer->sequence = seq ++;
+               file_buffer->noD = inode->noD;
+
+               byte = read_bytes(file, file_buffer->data, block_size);
+               if(byte == -1)
+                       goto read_err2;
+
+               file_buffer->size = byte;
+               file_buffer->file_size = -1;
+               file_buffer->error = FALSE;
+               file_buffer->fragment = FALSE;
+               bytes += byte;
+
+               if(byte == 0)
+                       break;
+
+               /*
+                * Update progress bar size.  This is done
+                * on every block rather than waiting for all blocks to be
+                * read incase write_file_process() is running in parallel
+                * with this.  Otherwise the current progress bar position
+                * may get ahead of the progress bar size.
+                */ 
+               progress_bar_size(1);
+
+               if(prev_buffer)
+                       put_file_buffer(prev_buffer);
+               prev_buffer = file_buffer;
+       }
+
+       /*
+        * Update inode file size now that the size of the dynamic pseudo file
+        * is known.  This is needed for the -info option.
+        */
+       inode->buf.st_size = bytes;
+
+       res = waitpid(child, &status, 0);
+       close(file);
+
+       if(res == -1 || !WIFEXITED(status) || WEXITSTATUS(status) != 0)
+               goto read_err;
+
+       if(prev_buffer == NULL)
+               prev_buffer = file_buffer;
+       else {
+               cache_block_put(file_buffer);
+               seq --;
+       }
+       prev_buffer->file_size = bytes;
+       prev_buffer->fragment = is_fragment(inode);
+       put_file_buffer(prev_buffer);
+
+       return;
+
+read_err2:
+       close(file);
+read_err:
+       if(prev_buffer) {
+               cache_block_put(file_buffer);
+               seq --;
+               file_buffer = prev_buffer;
+       }
+       file_buffer->error = TRUE;
+       put_file_buffer(file_buffer);
+}
+
+
+void reader_read_file(struct dir_ent *dir_ent)
+{
+       struct stat *buf = &dir_ent->inode->buf, buf2;
+       struct file_buffer *file_buffer;
+       int blocks, file, res;
+       long long bytes, read_size;
+       struct inode_info *inode = dir_ent->inode;
+
+       if(inode->read)
+               return;
+
+       inode->read = TRUE;
+again:
+       bytes = 0;
+       read_size = buf->st_size;
+       blocks = (read_size + block_size - 1) >> block_log;
+
+       file = open(pathname_reader(dir_ent), O_RDONLY);
+       if(file == -1) {
+               file_buffer = cache_get_nohash(reader_buffer);
+               file_buffer->sequence = seq ++;
+               goto read_err2;
+       }
+
+       do {
+               file_buffer = cache_get_nohash(reader_buffer);
+               file_buffer->file_size = read_size;
+               file_buffer->sequence = seq ++;
+               file_buffer->noD = inode->noD;
+               file_buffer->error = FALSE;
+
+               /*
+                * Always try to read block_size bytes from the file rather
+                * than expected bytes (which will be less than the block_size
+                * at the file tail) to check that the file hasn't grown
+                * since being stated.  If it is longer (or shorter) than
+                * expected, then restat, and try again.  Note the special
+                * case where the file is an exact multiple of the block_size
+                * is dealt with later.
+                */
+               file_buffer->size = read_bytes(file, file_buffer->data,
+                       block_size);
+               if(file_buffer->size == -1)
+                       goto read_err;
+
+               bytes += file_buffer->size;
+
+               if(blocks > 1) {
+                       /* non-tail block should be exactly block_size */
+                       if(file_buffer->size < block_size)
+                               goto restat;
+
+                       file_buffer->fragment = FALSE;
+                       put_file_buffer(file_buffer);
+               }
+       } while(-- blocks > 0);
+
+       /* Overall size including tail should match */
+       if(read_size != bytes)
+               goto restat;
+
+       if(read_size && read_size % block_size == 0) {
+               /*
+                * Special case where we've not tried to read past the end of
+                * the file.  We expect to get EOF, i.e. the file isn't larger
+                * than we expect.
+                */
+               char buffer;
+               int res;
+
+               res = read_bytes(file, &buffer, 1);
+               if(res == -1)
+                       goto read_err;
+
+               if(res != 0)
+                       goto restat;
+       }
+
+       file_buffer->fragment = is_fragment(inode);
+       put_file_buffer(file_buffer);
+
+       close(file);
+
+       return;
+
+restat:
+       res = fstat(file, &buf2);
+       if(res == -1) {
+               ERROR("Cannot stat dir/file %s because %s\n",
+                       pathname_reader(dir_ent), strerror(errno));
+               goto read_err;
+       }
+
+       if(read_size != buf2.st_size) {
+               close(file);
+               memcpy(buf, &buf2, sizeof(struct stat));
+               file_buffer->error = 2;
+               put_file_buffer(file_buffer);
+               goto again;
+       }
+read_err:
+       close(file);
+read_err2:
+       file_buffer->error = TRUE;
+       put_file_buffer(file_buffer);
+}
+
+
+void reader_scan(struct dir_info *dir) {
+       struct dir_ent *dir_ent = dir->list;
+
+       for(; dir_ent; dir_ent = dir_ent->next) {
+               struct stat *buf = &dir_ent->inode->buf;
+               if(dir_ent->inode->root_entry)
+                       continue;
+
+               if(IS_PSEUDO_PROCESS(dir_ent->inode)) {
+                       reader_read_process(dir_ent);
+                       continue;
+               }
+
+               switch(buf->st_mode & S_IFMT) {
+                       case S_IFREG:
+                               reader_read_file(dir_ent);
+                               break;
+                       case S_IFDIR:
+                               reader_scan(dir_ent->dir);
+                               break;
+               }
+       }
+}
+
+
+void *reader(void *arg)
+{
+       if(!sorted)
+               reader_scan(queue_get(to_reader));
+       else {
+               int i;
+               struct priority_entry *entry;
+
+               queue_get(to_reader);
+               for(i = 65535; i >= 0; i--)
+                       for(entry = priority_list[i]; entry;
+                                                       entry = entry->next)
+                               reader_read_file(entry->dir);
+       }
+
+       pthread_exit(NULL);
+}
+
+
+void *writer(void *arg)
+{
+       while(1) {
+               struct file_buffer *file_buffer = queue_get(to_writer);
+               off_t off;
+
+               if(file_buffer == NULL) {
+                       queue_put(from_writer, NULL);
+                       continue;
+               }
+
+               off = file_buffer->block;
+
+               pthread_cleanup_push((void *) pthread_mutex_unlock, &pos_mutex);
+               pthread_mutex_lock(&pos_mutex);
+
+               if(lseek(fd, start_offset + off, SEEK_SET) == -1) {
+                       ERROR("writer: Lseek on destination failed because "
+                               "%s, offset=0x%llx\n", strerror(errno), start_offset + off);
+                       BAD_ERROR("Probably out of space on output "
+                               "%s\n", block_device ? "block device" :
+                               "filesystem");
+               }
+
+               if(write_bytes(fd, file_buffer->data,
+                               file_buffer->size) == -1)
+                       BAD_ERROR("Failed to write to output %s\n",
+                               block_device ? "block device" : "filesystem");
+
+               pthread_cleanup_pop(1);
+
+               cache_block_put(file_buffer);
+       }
+}
+
+
+int all_zero(struct file_buffer *file_buffer)
+{
+       int i;
+       long entries = file_buffer->size / sizeof(long);
+       long *p = (long *) file_buffer->data;
+
+       for(i = 0; i < entries && p[i] == 0; i++);
+
+       if(i == entries) {
+               for(i = file_buffer->size & ~(sizeof(long) - 1);
+                       i < file_buffer->size && file_buffer->data[i] == 0;
+                       i++);
+
+               return i == file_buffer->size;
+       }
+
+       return 0;
+}
+
+
+void *deflator(void *arg)
+{
+       struct file_buffer *write_buffer = cache_get_nohash(bwriter_buffer);
+       void *stream = NULL;
+       int res;
+
+       res = compressor_init(comp, &stream, block_size, 1);
+       if(res)
+               BAD_ERROR("deflator:: compressor_init failed\n");
+
+       while(1) {
+               struct file_buffer *file_buffer = queue_get(to_deflate);
+
+               if(sparse_files && all_zero(file_buffer)) { 
+                       file_buffer->c_byte = 0;
+                       seq_queue_put(to_main, file_buffer);
+               } else {
+                       write_buffer->c_byte = mangle2(stream,
+                               write_buffer->data, file_buffer->data,
+                               file_buffer->size, block_size,
+                               file_buffer->noD, 1);
+                       write_buffer->sequence = file_buffer->sequence;
+                       write_buffer->file_size = file_buffer->file_size;
+                       write_buffer->block = file_buffer->block;
+                       write_buffer->size = SQUASHFS_COMPRESSED_SIZE_BLOCK
+                               (write_buffer->c_byte);
+                       write_buffer->fragment = FALSE;
+                       write_buffer->error = FALSE;
+                       cache_block_put(file_buffer);
+                       seq_queue_put(to_main, write_buffer);
+                       write_buffer = cache_get_nohash(bwriter_buffer);
+               }
+       }
+}
+
+
+void *frag_deflator(void *arg)
+{
+       void *stream = NULL;
+       int res;
+
+       res = compressor_init(comp, &stream, block_size, 1);
+       if(res)
+               BAD_ERROR("frag_deflator:: compressor_init failed\n");
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex);
+
+       while(1) {
+               int c_byte, compressed_size;
+               struct file_buffer *file_buffer = queue_get(to_frag);
+               struct file_buffer *write_buffer =
+                       cache_get(fwriter_buffer, file_buffer->block);
+
+               c_byte = mangle2(stream, write_buffer->data, file_buffer->data,
+                       file_buffer->size, block_size, noF, 1);
+               compressed_size = SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte);
+               write_buffer->size = compressed_size;
+               pthread_mutex_lock(&fragment_mutex);
+               if(fragments_locked == FALSE) {
+                       fragment_table[file_buffer->block].size = c_byte;
+                       fragment_table[file_buffer->block].start_block = bytes;
+                       write_buffer->block = bytes;
+                       bytes += compressed_size;
+                       fragments_outstanding --;
+                       queue_put(to_writer, write_buffer);
+                       log_fragment(file_buffer->block, fragment_table[file_buffer->block].start_block);
+                       pthread_mutex_unlock(&fragment_mutex);
+                       TRACE("Writing fragment %lld, uncompressed size %d, "
+                               "compressed size %d\n", file_buffer->block,
+                               file_buffer->size, compressed_size);
+               } else {
+                               add_pending_fragment(write_buffer, c_byte,
+                                       file_buffer->block);
+                               pthread_mutex_unlock(&fragment_mutex);
+               }
+               cache_block_put(file_buffer);
+       }
+
+       pthread_cleanup_pop(0);
+}
+
+
+void *frag_order_deflator(void *arg)
+{
+       void *stream = NULL;
+       int res;
+
+       res = compressor_init(comp, &stream, block_size, 1);
+       if(res)
+               BAD_ERROR("frag_deflator:: compressor_init failed\n");
+
+       while(1) {
+               int c_byte;
+               struct file_buffer *file_buffer = queue_get(to_frag);
+               struct file_buffer *write_buffer =
+                       cache_get(fwriter_buffer, file_buffer->block);
+
+               c_byte = mangle2(stream, write_buffer->data, file_buffer->data,
+                       file_buffer->size, block_size, noF, 1);
+               write_buffer->block = file_buffer->block;
+               write_buffer->sequence = file_buffer->sequence;
+               write_buffer->size = c_byte;
+               write_buffer->fragment = FALSE;
+               seq_queue_put(to_order, write_buffer);
+               TRACE("Writing fragment %lld, uncompressed size %d, "
+                       "compressed size %d\n", file_buffer->block,
+                       file_buffer->size, SQUASHFS_COMPRESSED_SIZE_BLOCK(c_byte));
+               cache_block_put(file_buffer);
+       }
+}
+
+
+void *frag_orderer(void *arg)
+{
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex);
+
+       while(1) {
+               struct file_buffer *write_buffer = seq_queue_get(to_order);
+               int block = write_buffer->block;
+
+               pthread_mutex_lock(&fragment_mutex);
+               fragment_table[block].size = write_buffer->size;
+               fragment_table[block].start_block = bytes;
+               write_buffer->block = bytes;
+               bytes += SQUASHFS_COMPRESSED_SIZE_BLOCK(write_buffer->size);
+               write_buffer->size = SQUASHFS_COMPRESSED_SIZE_BLOCK(write_buffer->size);
+               fragments_outstanding --;
+               log_fragment(block, write_buffer->block);
+               queue_put(to_writer, write_buffer);
+               pthread_cond_signal(&fragment_waiting);
+               pthread_mutex_unlock(&fragment_mutex);
+       }
+
+       pthread_cleanup_pop(0);
+}
+
+
+struct file_buffer *get_file_buffer()
+{
+       struct file_buffer *file_buffer = seq_queue_get(to_main);
+
+       return file_buffer;
+}
+
+
+void write_file_empty(squashfs_inode *inode, struct dir_ent *dir_ent,
+       struct file_buffer *file_buffer, int *duplicate_file)
+{
+       file_count ++;
+       *duplicate_file = FALSE;
+       cache_block_put(file_buffer);
+       create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, 0, 0, 0,
+                NULL, &empty_fragment, NULL, 0);
+}
+
+
+void write_file_frag(squashfs_inode *inode, struct dir_ent *dir_ent,
+       struct file_buffer *file_buffer, int *duplicate_file)
+{
+       int size = file_buffer->file_size;
+       struct fragment *fragment;
+       unsigned short checksum = file_buffer->checksum;
+       char dont_put;
+
+       fragment = frag_duplicate(file_buffer, &dont_put);
+       *duplicate_file = !fragment;
+       if(!fragment) {
+               fragment = get_and_fill_fragment(file_buffer, dir_ent);
+               if(duplicate_checking)
+                       add_non_dup(size, 0, NULL, 0, fragment, 0, checksum,
+                               TRUE, TRUE);
+       }
+
+       if(dont_put)
+               free(file_buffer);
+       else
+               cache_block_put(file_buffer);
+
+       total_bytes += size;
+       file_count ++;
+
+       inc_progress_bar();
+
+       create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, size, 0,
+                       0, NULL, fragment, NULL, 0);
+
+       if(!duplicate_checking)
+               free_fragment(fragment);
+}
+
+
+void log_file(struct dir_ent *dir_ent, long long start)
+{
+       if(logging && start)
+               fprintf(log_fd, "%s, %lld\n", pathname(dir_ent), start);
+}
+
+
+int write_file_process(squashfs_inode *inode, struct dir_ent *dir_ent,
+       struct file_buffer *read_buffer, int *duplicate_file)
+{
+       long long read_size, file_bytes, start;
+       struct fragment *fragment;
+       unsigned int *block_list = NULL;
+       int block = 0, status;
+       long long sparse = 0;
+       struct file_buffer *fragment_buffer = NULL;
+
+       *duplicate_file = FALSE;
+
+       if(reproducible)
+               ensure_fragments_flushed();
+       else
+               lock_fragments();
+
+       file_bytes = 0;
+       start = bytes;
+       while (1) {
+               read_size = read_buffer->file_size;
+               if(read_buffer->fragment) {
+                       fragment_buffer = read_buffer;
+                       if(block == 0)
+                               start=0;
+               } else {
+                       block_list = realloc(block_list, (block + 1) *
+                               sizeof(unsigned int));
+                       if(block_list == NULL)
+                               MEM_ERROR();
+                       block_list[block ++] = read_buffer->c_byte;
+                       if(read_buffer->c_byte) {
+                               read_buffer->block = bytes;
+                               bytes += read_buffer->size;
+                               cache_hash(read_buffer, read_buffer->block);
+                               file_bytes += read_buffer->size;
+                               queue_put(to_writer, read_buffer);
+                       } else {
+                               sparse += read_buffer->size;
+                               cache_block_put(read_buffer);
+                       }
+               }
+               inc_progress_bar();
+
+               if(read_size != -1)
+                       break;
+
+               read_buffer = get_file_buffer();
+               if(read_buffer->error)
+                       goto read_err;
+       }
+
+       if(!reproducible)
+               unlock_fragments();
+
+       fragment = get_and_fill_fragment(fragment_buffer, dir_ent);
+
+       if(duplicate_checking)
+               add_non_dup(read_size, file_bytes, block_list, start, fragment,
+                       0, fragment_buffer ? fragment_buffer->checksum : 0,
+                       FALSE, TRUE);
+       cache_block_put(fragment_buffer);
+       file_count ++;
+       total_bytes += read_size;
+
+       create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, read_size, start,
+                block, block_list, fragment, NULL, sparse);
+       log_file(dir_ent, start);
+
+       if(duplicate_checking == FALSE) {
+               free(block_list);
+               free_fragment(fragment);
+       }
+
+       return 0;
+
+read_err:
+       dec_progress_bar(block);
+       status = read_buffer->error;
+       bytes = start;
+       if(!block_device) {
+               int res;
+
+               queue_put(to_writer, NULL);
+               if(queue_get(from_writer) != 0)
+                       EXIT_MKSQUASHFS();
+               res = ftruncate(fd, bytes);
+               if(res != 0)
+                       BAD_ERROR("Failed to truncate dest file because %s\n",
+                               strerror(errno));
+       }
+       if(!reproducible)
+               unlock_fragments();
+       free(block_list);
+       cache_block_put(read_buffer);
+       return status;
+}
+
+
+int write_file_blocks_dup(squashfs_inode *inode, struct dir_ent *dir_ent,
+       struct file_buffer *read_buffer, int *duplicate_file)
+{
+       int block, thresh;
+       long long read_size = read_buffer->file_size;
+       long long file_bytes, dup_start, start;
+       struct fragment *fragment;
+       struct file_info *dupl_ptr;
+       int blocks = (read_size + block_size - 1) >> block_log;
+       unsigned int *block_list, *block_listp;
+       struct file_buffer **buffer_list;
+       int status;
+       long long sparse = 0;
+       struct file_buffer *fragment_buffer = NULL;
+
+       block_list = malloc(blocks * sizeof(unsigned int));
+       if(block_list == NULL)
+               MEM_ERROR();
+       block_listp = block_list;
+
+       buffer_list = malloc(blocks * sizeof(struct file_buffer *));
+       if(buffer_list == NULL)
+               MEM_ERROR();
+
+       if(reproducible)
+               ensure_fragments_flushed();
+       else
+               lock_fragments();
+
+       file_bytes = 0;
+       start = dup_start = bytes;
+       thresh = blocks > bwriter_size ? blocks - bwriter_size : 0;
+
+       for(block = 0; block < blocks;) {
+               if(read_buffer->fragment) {
+                       block_list[block] = 0;
+                       buffer_list[block] = NULL;
+                       fragment_buffer = read_buffer;
+                       blocks = read_size >> block_log;
+               } else {
+                       block_list[block] = read_buffer->c_byte;
+
+                       if(read_buffer->c_byte) {
+                               read_buffer->block = bytes;
+                               bytes += read_buffer->size;
+                               file_bytes += read_buffer->size;
+                               cache_hash(read_buffer, read_buffer->block);
+                               if(block < thresh) {
+                                       buffer_list[block] = NULL;
+                                       queue_put(to_writer, read_buffer);
+                               } else
+                                       buffer_list[block] = read_buffer;
+                       } else {
+                               buffer_list[block] = NULL;
+                               sparse += read_buffer->size;
+                               cache_block_put(read_buffer);
+                       }
+               }
+               inc_progress_bar();
+
+               if(++block < blocks) {
+                       read_buffer = get_file_buffer();
+                       if(read_buffer->error)
+                               goto read_err;
+               }
+       }
+
+       dupl_ptr = duplicate(read_size, file_bytes, &block_listp, &dup_start,
+               &fragment, fragment_buffer, blocks, 0, FALSE);
+
+       if(dupl_ptr) {
+               *duplicate_file = FALSE;
+               for(block = thresh; block < blocks; block ++)
+                       if(buffer_list[block])
+                               queue_put(to_writer, buffer_list[block]);
+               fragment = get_and_fill_fragment(fragment_buffer, dir_ent);
+               dupl_ptr->fragment = fragment;
+       } else {
+               *duplicate_file = TRUE;
+               for(block = thresh; block < blocks; block ++)
+                       cache_block_put(buffer_list[block]);
+               bytes = start;
+               if(thresh && !block_device) {
+                       int res;
+
+                       queue_put(to_writer, NULL);
+                       if(queue_get(from_writer) != 0)
+                               EXIT_MKSQUASHFS();
+                       res = ftruncate(fd, bytes);
+                       if(res != 0)
+                               BAD_ERROR("Failed to truncate dest file because"
+                                       "  %s\n", strerror(errno));
+               }
+       }
+
+       if(!reproducible)
+               unlock_fragments();
+       cache_block_put(fragment_buffer);
+       free(buffer_list);
+       file_count ++;
+       total_bytes += read_size;
+
+       /*
+        * sparse count is needed to ensure squashfs correctly reports a
+        * a smaller block count on stat calls to sparse files.  This is
+        * to ensure intelligent applications like cp correctly handle the
+        * file as a sparse file.  If the file in the original filesystem isn't
+        * stored as a sparse file then still store it sparsely in squashfs, but
+        * report it as non-sparse on stat calls to preserve semantics
+        */
+       if(sparse && (dir_ent->inode->buf.st_blocks << 9) >= read_size)
+               sparse = 0;
+
+       create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, read_size,
+               dup_start, blocks, block_listp, fragment, NULL, sparse);
+
+       if(*duplicate_file == TRUE)
+               free(block_list);
+       else
+               log_file(dir_ent, dup_start);
+
+       return 0;
+
+read_err:
+       dec_progress_bar(block);
+       status = read_buffer->error;
+       bytes = start;
+       if(thresh && !block_device) {
+               int res;
+
+               queue_put(to_writer, NULL);
+               if(queue_get(from_writer) != 0)
+                       EXIT_MKSQUASHFS();
+               res = ftruncate(fd, bytes);
+               if(res != 0)
+                       BAD_ERROR("Failed to truncate dest file because %s\n",
+                               strerror(errno));
+       }
+       if(!reproducible)
+               unlock_fragments();
+       for(blocks = thresh; blocks < block; blocks ++)
+               cache_block_put(buffer_list[blocks]);
+       free(buffer_list);
+       free(block_list);
+       cache_block_put(read_buffer);
+       return status;
+}
+
+
+int write_file_blocks(squashfs_inode *inode, struct dir_ent *dir_ent,
+       struct file_buffer *read_buffer, int *dup)
+{
+       long long read_size = read_buffer->file_size;
+       long long file_bytes, start;
+       struct fragment *fragment;
+       unsigned int *block_list;
+       int block, status;
+       int blocks = (read_size + block_size - 1) >> block_log;
+       long long sparse = 0;
+       struct file_buffer *fragment_buffer = NULL;
+
+       if(pre_duplicate(read_size))
+               return write_file_blocks_dup(inode, dir_ent, read_buffer, dup);
+
+       *dup = FALSE;
+
+       block_list = malloc(blocks * sizeof(unsigned int));
+       if(block_list == NULL)
+               MEM_ERROR();
+
+       if(reproducible)
+               ensure_fragments_flushed();
+       else
+               lock_fragments();
+
+       file_bytes = 0;
+       start = bytes;
+       for(block = 0; block < blocks;) {
+               if(read_buffer->fragment) {
+                       block_list[block] = 0;
+                       fragment_buffer = read_buffer;
+                       blocks = read_size >> block_log;
+               } else {
+                       block_list[block] = read_buffer->c_byte;
+                       if(read_buffer->c_byte) {
+                               read_buffer->block = bytes;
+                               bytes += read_buffer->size;
+                               cache_hash(read_buffer, read_buffer->block);
+                               file_bytes += read_buffer->size;
+                               queue_put(to_writer, read_buffer);
+                       } else {
+                               sparse += read_buffer->size;
+                               cache_block_put(read_buffer);
+                       }
+               }
+               inc_progress_bar();
+
+               if(++block < blocks) {
+                       read_buffer = get_file_buffer();
+                       if(read_buffer->error)
+                               goto read_err;
+               }
+       }
+
+       if(!reproducible)
+               unlock_fragments();
+       fragment = get_and_fill_fragment(fragment_buffer, dir_ent);
+
+       if(duplicate_checking)
+               add_non_dup(read_size, file_bytes, block_list, start, fragment,
+                       0, fragment_buffer ? fragment_buffer->checksum : 0,
+                       FALSE, TRUE);
+       cache_block_put(fragment_buffer);
+       file_count ++;
+       total_bytes += read_size;
+
+       /*
+        * sparse count is needed to ensure squashfs correctly reports a
+        * a smaller block count on stat calls to sparse files.  This is
+        * to ensure intelligent applications like cp correctly handle the
+        * file as a sparse file.  If the file in the original filesystem isn't
+        * stored as a sparse file then still store it sparsely in squashfs, but
+        * report it as non-sparse on stat calls to preserve semantics
+        */
+       if(sparse && (dir_ent->inode->buf.st_blocks << 9) >= read_size)
+               sparse = 0;
+
+       create_inode(inode, NULL, dir_ent, SQUASHFS_FILE_TYPE, read_size, start,
+                blocks, block_list, fragment, NULL, sparse);
+       log_file(dir_ent, start);
+
+       if(duplicate_checking == FALSE) {
+               free(block_list);
+               free_fragment(fragment);
+       }
+
+       return 0;
+
+read_err:
+       dec_progress_bar(block);
+       status = read_buffer->error;
+       bytes = start;
+       if(!block_device) {
+               int res;
+
+               queue_put(to_writer, NULL);
+               if(queue_get(from_writer) != 0)
+                       EXIT_MKSQUASHFS();
+               res = ftruncate(fd, bytes);
+               if(res != 0)
+                       BAD_ERROR("Failed to truncate dest file because %s\n",
+                               strerror(errno));
+       }
+       if(!reproducible)
+               unlock_fragments();
+       free(block_list);
+       cache_block_put(read_buffer);
+       return status;
+}
+
+
+void write_file(squashfs_inode *inode, struct dir_ent *dir, int *dup)
+{
+       int status;
+       struct file_buffer *read_buffer;
+
+again:
+       read_buffer = get_file_buffer();
+       status = read_buffer->error;
+
+       if(status)
+               cache_block_put(read_buffer);
+       else if(read_buffer->file_size == -1)
+               status = write_file_process(inode, dir, read_buffer, dup);
+       else if(read_buffer->file_size == 0)
+               write_file_empty(inode, dir, read_buffer, dup);
+       else if(read_buffer->fragment && read_buffer->c_byte)
+               write_file_frag(inode, dir, read_buffer, dup);
+       else
+               status = write_file_blocks(inode, dir, read_buffer, dup);
+
+       if(status == 2) {
+               ERROR("File %s changed size while reading filesystem, "
+                       "attempting to re-read\n", pathname(dir));
+               goto again;
+       } else if(status == 1) {
+               ERROR_START("Failed to read file %s", pathname(dir));
+               ERROR_EXIT(", creating empty file\n");
+               write_file_empty(inode, dir, NULL, dup);
+       }
+}
+
+
+#define BUFF_SIZE 512
+char *name;
+char *basename_r();
+
+char *getbase(char *pathname)
+{
+       static char *b_buffer = NULL;
+       static int b_size = BUFF_SIZE;
+       char *result;
+
+       if(b_buffer == NULL) {
+               b_buffer = malloc(b_size);
+               if(b_buffer == NULL)
+                       MEM_ERROR();
+       }
+
+       while(1) {
+               if(*pathname != '/') {
+                       result = getcwd(b_buffer, b_size);
+                       if(result == NULL && errno != ERANGE)
+                               BAD_ERROR("Getcwd failed in getbase\n");
+
+                       /* enough room for pathname + "/" + '\0' terminator? */
+                       if(result && strlen(pathname) + 2 <=
+                                               b_size - strlen(b_buffer)) {
+                               strcat(strcat(b_buffer, "/"), pathname);
+                               break;
+                       }
+               } else if(strlen(pathname) < b_size) {
+                       strcpy(b_buffer, pathname);
+                       break;
+               }
+
+               /* Buffer not large enough, realloc and try again */
+               b_buffer = realloc(b_buffer, b_size += BUFF_SIZE);
+               if(b_buffer == NULL)
+                       MEM_ERROR();
+       }
+
+       name = b_buffer;
+       if(((result = basename_r()) == NULL) || (strcmp(result, "..") == 0))
+               return NULL;
+       else
+               return result;
+}
+
+
+char *basename_r()
+{
+       char *s;
+       char *p;
+       int n = 1;
+
+       for(;;) {
+               s = name;
+               if(*name == '\0')
+                       return NULL;
+               if(*name != '/') {
+                       while(*name != '\0' && *name != '/') name++;
+                       n = name - s;
+               }
+               while(*name == '/') name++;
+               if(strncmp(s, ".", n) == 0)
+                       continue;
+               if((*name == '\0') || (strncmp(s, "..", n) == 0) ||
+                               ((p = basename_r()) == NULL)) {
+                       s[n] = '\0';
+                       return s;
+               }
+               if(strcmp(p, "..") == 0)
+                       continue;
+               return p;
+       }
+}
+
+
+struct inode_info *lookup_inode3(struct stat *buf, int pseudo, int id,
+       char *symlink, int bytes)
+{
+       int ino_hash = INODE_HASH(buf->st_dev, buf->st_ino);
+       struct inode_info *inode;
+
+       /*
+        * Look-up inode in hash table, if it already exists we have a
+        * hard-link, so increment the nlink count and return it.
+        * Don't do the look-up for directories because we don't hard-link
+        * directories.
+        */
+       if ((buf->st_mode & S_IFMT) != S_IFDIR) {
+               for(inode = inode_info[ino_hash]; inode; inode = inode->next) {
+                       if(memcmp(buf, &inode->buf, sizeof(struct stat)) == 0) {
+                               inode->nlink ++;
+                               return inode;
+                       }
+               }
+       }
+
+       inode = malloc(sizeof(struct inode_info) + bytes);
+       if(inode == NULL)
+               MEM_ERROR();
+
+       if(bytes)
+               memcpy(&inode->symlink, symlink, bytes);
+       memcpy(&inode->buf, buf, sizeof(struct stat));
+       inode->read = FALSE;
+       inode->root_entry = FALSE;
+       inode->pseudo_file = pseudo;
+       inode->pseudo_id = id;
+       inode->inode = SQUASHFS_INVALID_BLK;
+       inode->nlink = 1;
+       inode->inode_number = 0;
+
+       /*
+        * Copy filesystem wide defaults into inode, these filesystem
+        * wide defaults may be altered on an individual inode basis by
+        * user specified actions
+        *
+       */
+       inode->no_fragments = no_fragments;
+       inode->always_use_fragments = always_use_fragments;
+       inode->noD = noD;
+       inode->noF = noF;
+
+       inode->next = inode_info[ino_hash];
+       inode_info[ino_hash] = inode;
+
+       return inode;
+}
+
+
+struct inode_info *lookup_inode2(struct stat *buf, int pseudo, int id)
+{
+       return lookup_inode3(buf, pseudo, id, NULL, 0);
+}
+
+
+static inline struct inode_info *lookup_inode(struct stat *buf)
+{
+       return lookup_inode2(buf, 0, 0);
+}
+
+
+static inline void alloc_inode_no(struct inode_info *inode, unsigned int use_this)
+{
+       if (inode->inode_number == 0) {
+               inode->inode_number = use_this ? : inode_no ++;
+               if((inode->buf.st_mode & S_IFMT) == S_IFREG)
+                       progress_bar_size((inode->buf.st_size + block_size - 1)
+                                                                >> block_log);
+       }
+}
+
+
+static inline struct dir_ent *create_dir_entry(char *name, char *source_name,
+       char *nonstandard_pathname, struct dir_info *dir)
+{
+       struct dir_ent *dir_ent = malloc(sizeof(struct dir_ent));
+       if(dir_ent == NULL)
+               MEM_ERROR();
+
+       dir_ent->name = name;
+       dir_ent->source_name = source_name;
+       dir_ent->nonstandard_pathname = nonstandard_pathname;
+       dir_ent->our_dir = dir;
+       dir_ent->inode = NULL;
+       dir_ent->next = NULL;
+
+       return dir_ent;
+}
+
+
+static inline void add_dir_entry(struct dir_ent *dir_ent, struct dir_info *sub_dir,
+       struct inode_info *inode_info)
+{
+       struct dir_info *dir = dir_ent->our_dir;
+
+       if(sub_dir)
+               sub_dir->dir_ent = dir_ent;
+       dir_ent->inode = inode_info;
+       dir_ent->dir = sub_dir;
+
+       dir_ent->next = dir->list;
+       dir->list = dir_ent;
+       dir->count++;
+}
+
+
+static inline void add_dir_entry2(char *name, char *source_name,
+       char *nonstandard_pathname, struct dir_info *sub_dir,
+       struct inode_info *inode_info, struct dir_info *dir)
+{
+       struct dir_ent *dir_ent = create_dir_entry(name, source_name,
+               nonstandard_pathname, dir);
+
+
+       add_dir_entry(dir_ent, sub_dir, inode_info);
+}
+
+
+static inline void free_dir_entry(struct dir_ent *dir_ent)
+{
+       if(dir_ent->name)
+               free(dir_ent->name);
+
+       if(dir_ent->source_name)
+               free(dir_ent->source_name);
+
+       if(dir_ent->nonstandard_pathname)
+               free(dir_ent->nonstandard_pathname);
+
+       /* if this entry has been associated with an inode, then we need
+        * to update the inode nlink count.  Orphaned inodes are harmless, and
+        * is easier to leave them than go to the bother of deleting them */
+       if(dir_ent->inode && !dir_ent->inode->root_entry)
+               dir_ent->inode->nlink --;
+
+       free(dir_ent);
+}
+
+
+static inline void add_excluded(struct dir_info *dir)
+{
+       dir->excluded ++;
+}
+
+
+void dir_scan(squashfs_inode *inode, char *pathname,
+       struct dir_ent *(_readdir)(struct dir_info *), int progress)
+{
+       struct stat buf;
+       struct dir_ent *dir_ent;
+       
+       root_dir = dir_scan1(pathname, "", paths, _readdir, 1);
+       if(root_dir == NULL)
+               return;
+
+       /* Create root directory dir_ent and associated inode, and connect
+        * it to the root directory dir_info structure */
+       dir_ent = create_dir_entry("", NULL, pathname,
+                                               scan1_opendir("", "", 0));
+
+       if(pathname[0] == '\0') {
+               /*
+                * dummy top level directory, if multiple sources specified on
+                * command line
+                */
+               memset(&buf, 0, sizeof(buf));
+               buf.st_mode = (root_mode_opt) ? root_mode | S_IFDIR : S_IRWXU | S_IRWXG | S_IRWXO | S_IFDIR;
+               buf.st_uid = getuid();
+               buf.st_gid = getgid();
+               buf.st_mtime = time(NULL);
+               buf.st_dev = 0;
+               buf.st_ino = 0;
+               dir_ent->inode = lookup_inode2(&buf, PSEUDO_FILE_OTHER, 0);
+       } else {
+               if(lstat(pathname, &buf) == -1)
+                       /* source directory has disappeared? */
+                       BAD_ERROR("Cannot stat source directory %s because %s\n",
+                               pathname, strerror(errno));
+               if(root_mode_opt)
+                       buf.st_mode = root_mode | S_IFDIR;
+
+               dir_ent->inode = lookup_inode(&buf);
+       }
+
+       dir_ent->dir = root_dir;
+       root_dir->dir_ent = dir_ent;
+
+       /*
+        * Process most actions and any pseudo files
+        */
+       if(actions() || get_pseudo())
+               dir_scan2(root_dir, get_pseudo());
+
+       /*
+        * Process move actions
+        */
+       if(move_actions()) {
+               dir_scan3(root_dir);
+               do_move_actions();
+       }
+
+       /*
+        * Process prune actions
+        */
+       if(prune_actions())
+               dir_scan4(root_dir);
+
+       /*
+        * Process empty actions
+        */
+       if(empty_actions())
+               dir_scan5(root_dir);
+
+       /*
+        * Sort directories and compute the inode numbers
+        */
+       dir_scan6(root_dir);
+
+       alloc_inode_no(dir_ent->inode, root_inode_number);
+
+       eval_actions(root_dir, dir_ent);
+
+       if(sorted)
+               generate_file_priorities(root_dir, 0,
+                       &root_dir->dir_ent->inode->buf);
+
+       if(appending) {
+               sigset_t sigmask;
+
+               restore_thread = init_restore_thread();
+               sigemptyset(&sigmask);
+               sigaddset(&sigmask, SIGINT);
+               sigaddset(&sigmask, SIGTERM);
+               sigaddset(&sigmask, SIGUSR1);
+               if(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) != 0)
+                       BAD_ERROR("Failed to set signal mask\n");
+               write_destination(fd, SQUASHFS_START, 4, "\0\0\0\0");
+       }
+
+       queue_put(to_reader, root_dir);
+
+       set_progressbar_state(progress);
+
+       if(sorted)
+               sort_files_and_write(root_dir);
+
+       dir_scan7(inode, root_dir);
+       dir_ent->inode->inode = *inode;
+       dir_ent->inode->type = SQUASHFS_DIR_TYPE;
+}
+
+
+/*
+ * dir_scan1 routines...
+ * These scan the source directories into memory for processing.
+ * Exclude actions are processed here (in contrast to the other actions)
+ * because they affect what is scanned.
+ */
+struct dir_info *scan1_opendir(char *pathname, char *subpath, int depth)
+{
+       struct dir_info *dir;
+
+       dir = malloc(sizeof(struct dir_info));
+       if(dir == NULL)
+               MEM_ERROR();
+
+       if(pathname[0] != '\0') {
+               dir->linuxdir = opendir(pathname);
+               if(dir->linuxdir == NULL) {
+                       free(dir);
+                       return NULL;
+               }
+       }
+
+       dir->pathname = strdup(pathname);
+       dir->subpath = strdup(subpath);
+       dir->count = 0;
+       dir->directory_count = 0;
+       dir->dir_is_ldir = TRUE;
+       dir->list = NULL;
+       dir->depth = depth;
+       dir->excluded = 0;
+
+       return dir;
+}
+
+
+struct dir_ent *scan1_encomp_readdir(struct dir_info *dir)
+{
+       static int index = 0;
+
+       if(dir->count < old_root_entries) {
+               int i;
+
+               for(i = 0; i < old_root_entries; i++) {
+                       if(old_root_entry[i].inode.type == SQUASHFS_DIR_TYPE)
+                               dir->directory_count ++;
+                       add_dir_entry2(old_root_entry[i].name, NULL, NULL, NULL,
+                               &old_root_entry[i].inode, dir);
+               }
+       }
+
+       while(index < source) {
+               char *basename = NULL;
+               char *dir_name = getbase(source_path[index]);
+               int pass = 1, res;
+
+               if(dir_name == NULL) {
+                       ERROR_START("Bad source directory %s",
+                               source_path[index]);
+                       ERROR_EXIT(" - skipping ...\n");
+                       index ++;
+                       continue;
+               }
+               dir_name = strdup(dir_name);
+               for(;;) {
+                       struct dir_ent *dir_ent = dir->list;
+
+                       for(; dir_ent && strcmp(dir_ent->name, dir_name) != 0;
+                               dir_ent = dir_ent->next);
+                       if(dir_ent == NULL)
+                               break;
+                       ERROR("Source directory entry %s already used! - trying"
+                               " ", dir_name);
+                       if(pass == 1)
+                               basename = dir_name;
+                       else
+                               free(dir_name);
+                       res = asprintf(&dir_name, "%s_%d", basename, pass++);
+                       if(res == -1)
+                               BAD_ERROR("asprintf failed in "
+                                       "scan1_encomp_readdir\n");
+                       ERROR("%s\n", dir_name);
+               }
+               return create_dir_entry(dir_name, basename,
+                       strdup(source_path[index ++]), dir);
+       }
+       return NULL;
+}
+
+
+struct dir_ent *scan1_single_readdir(struct dir_info *dir)
+{
+       struct dirent *d_name;
+       int i;
+
+       if(dir->count < old_root_entries) {
+               for(i = 0; i < old_root_entries; i++) {
+                       if(old_root_entry[i].inode.type == SQUASHFS_DIR_TYPE)
+                               dir->directory_count ++;
+                       add_dir_entry2(old_root_entry[i].name, NULL, NULL, NULL,
+                               &old_root_entry[i].inode, dir);
+               }
+       }
+
+       if((d_name = readdir(dir->linuxdir)) != NULL) {
+               char *basename = NULL;
+               char *dir_name = strdup(d_name->d_name);
+               int pass = 1, res;
+
+               for(;;) {
+                       struct dir_ent *dir_ent = dir->list;
+
+                       for(; dir_ent && strcmp(dir_ent->name, dir_name) != 0;
+                               dir_ent = dir_ent->next);
+                       if(dir_ent == NULL)
+                               break;
+                       ERROR("Source directory entry %s already used! - trying"
+                               " ", dir_name);
+                       if (pass == 1)
+                               basename = dir_name;
+                       else
+                               free(dir_name);
+                       res = asprintf(&dir_name, "%s_%d", d_name->d_name, pass++);
+                       if(res == -1)
+                               BAD_ERROR("asprintf failed in "
+                                       "scan1_single_readdir\n");
+                       ERROR("%s\n", dir_name);
+               }
+               return create_dir_entry(dir_name, basename, NULL, dir);
+       }
+
+       return NULL;
+}
+
+
+struct dir_ent *scan1_readdir(struct dir_info *dir)
+{
+       struct dirent *d_name = readdir(dir->linuxdir);
+
+       return d_name ?
+               create_dir_entry(strdup(d_name->d_name), NULL, NULL, dir) :
+               NULL;
+}
+
+
+void scan1_freedir(struct dir_info *dir)
+{
+       if(dir->pathname[0] != '\0')
+               closedir(dir->linuxdir);
+}
+
+
+struct dir_info *dir_scan1(char *filename, char *subpath,
+       struct pathnames *paths,
+       struct dir_ent *(_readdir)(struct dir_info *), int depth)
+{
+       struct dir_info *dir = scan1_opendir(filename, subpath, depth);
+       struct dir_ent *dir_ent;
+
+       if(dir == NULL) {
+               ERROR_START("Could not open %s", filename);
+               ERROR_EXIT(", skipping...\n");
+               return NULL;
+       }
+
+       while((dir_ent = _readdir(dir))) {
+               struct dir_info *sub_dir;
+               struct stat buf;
+               struct pathnames *new = NULL;
+               char *filename = pathname(dir_ent);
+               char *subpath = NULL;
+               char *dir_name = dir_ent->name;
+
+               if(strcmp(dir_name, ".") == 0 || strcmp(dir_name, "..") == 0) {
+                       free_dir_entry(dir_ent);
+                       continue;
+               }
+
+               if(lstat(filename, &buf) == -1) {
+                       ERROR_START("Cannot stat dir/file %s because %s",
+                               filename, strerror(errno));
+                       ERROR_EXIT(", ignoring\n");
+                       free_dir_entry(dir_ent);
+                       continue;
+               }
+
+               if((buf.st_mode & S_IFMT) != S_IFREG &&
+                                       (buf.st_mode & S_IFMT) != S_IFDIR &&
+                                       (buf.st_mode & S_IFMT) != S_IFLNK &&
+                                       (buf.st_mode & S_IFMT) != S_IFCHR &&
+                                       (buf.st_mode & S_IFMT) != S_IFBLK &&
+                                       (buf.st_mode & S_IFMT) != S_IFIFO &&
+                                       (buf.st_mode & S_IFMT) != S_IFSOCK) {
+                       ERROR_START("File %s has unrecognised filetype %d",
+                               filename, buf.st_mode & S_IFMT);
+                       ERROR_EXIT(", ignoring\n");
+                       free_dir_entry(dir_ent);
+                       continue;
+               }
+
+               if((old_exclude && old_excluded(filename, &buf)) ||
+                       (!old_exclude && excluded(dir_name, paths, &new))) {
+                       add_excluded(dir);
+                       free_dir_entry(dir_ent);
+                       continue;
+               }
+
+               if(exclude_actions()) {
+                       subpath = subpathname(dir_ent);
+                       
+                       if(eval_exclude_actions(dir_name, filename, subpath,
+                                                       &buf, depth, dir_ent)) {
+                               add_excluded(dir);
+                               free_dir_entry(dir_ent);
+                               continue;
+                       }
+               }
+
+               switch(buf.st_mode & S_IFMT) {
+               case S_IFDIR:
+                       if(subpath == NULL)
+                               subpath = subpathname(dir_ent);
+
+                       sub_dir = dir_scan1(filename, subpath, new,
+                                       scan1_readdir, depth + 1);
+                       if(sub_dir) {
+                               dir->directory_count ++;
+                               add_dir_entry(dir_ent, sub_dir,
+                                                       lookup_inode(&buf));
+                       } else
+                               free_dir_entry(dir_ent);
+                       break;
+               case S_IFLNK: {
+                       int byte;
+                       static char buff[65536]; /* overflow safe */
+
+                       byte = readlink(filename, buff, 65536);
+                       if(byte == -1) {
+                               ERROR_START("Failed to read symlink %s",
+                                                               filename);
+                               ERROR_EXIT(", ignoring\n");
+                       } else if(byte == 65536) {
+                               ERROR_START("Symlink %s is greater than 65536 "
+                                                       "bytes!", filename);
+                               ERROR_EXIT(", ignoring\n");
+                       } else {
+                               /* readlink doesn't 0 terminate the returned
+                                * path */
+                               buff[byte] = '\0';
+                               add_dir_entry(dir_ent, NULL, lookup_inode3(&buf,
+                                                        0, 0, buff, byte + 1));
+                       }
+                       break;
+               }
+               default:
+                       add_dir_entry(dir_ent, NULL, lookup_inode(&buf));
+               }
+
+               free(new);
+       }
+
+       scan1_freedir(dir);
+
+       return dir;
+}
+
+
+/*
+ * dir_scan2 routines...
+ * This processes most actions and any pseudo files
+ */
+struct dir_ent *scan2_readdir(struct dir_info *dir, struct dir_ent *dir_ent)
+{
+       if (dir_ent == NULL)
+               dir_ent = dir->list;
+       else
+               dir_ent = dir_ent->next;
+
+       for(; dir_ent && dir_ent->inode->root_entry; dir_ent = dir_ent->next);
+
+       return dir_ent; 
+}
+
+
+struct dir_ent *scan2_lookup(struct dir_info *dir, char *name)
+{
+       struct dir_ent *dir_ent = dir->list;
+
+       for(; dir_ent && strcmp(dir_ent->name, name) != 0;
+                                       dir_ent = dir_ent->next);
+
+       return dir_ent;
+}
+
+
+void dir_scan2(struct dir_info *dir, struct pseudo *pseudo)
+{
+       struct dir_ent *dir_ent = NULL;
+       struct pseudo_entry *pseudo_ent;
+       struct stat buf;
+       static int pseudo_ino = 1;
+       
+       while((dir_ent = scan2_readdir(dir, dir_ent)) != NULL) {
+               struct inode_info *inode_info = dir_ent->inode;
+               struct stat *buf = &inode_info->buf;
+               char *name = dir_ent->name;
+
+               eval_actions(root_dir, dir_ent);
+
+               if((buf->st_mode & S_IFMT) == S_IFDIR)
+                       dir_scan2(dir_ent->dir, pseudo_subdir(name, pseudo));
+       }
+
+       while((pseudo_ent = pseudo_readdir(pseudo)) != NULL) {
+               dir_ent = scan2_lookup(dir, pseudo_ent->name);
+               if(pseudo_ent->dev->type == 'm') {
+                       struct stat *buf;
+                       if(dir_ent == NULL) {
+                               ERROR_START("Pseudo modify file \"%s\" does "
+                                       "not exist in source filesystem.",
+                                       pseudo_ent->pathname);
+                               ERROR_EXIT("  Ignoring.\n");
+                               continue;
+                       }
+                       if(dir_ent->inode->root_entry) {
+                               ERROR_START("Pseudo modify file \"%s\" is a "
+                                       "pre-existing file in the filesystem "
+                                       "being appended to.  It cannot be "\
+                                       "modified.", pseudo_ent->pathname);
+                               ERROR_EXIT("  Ignoring.\n");
+                               continue;
+                       }
+                       buf = &dir_ent->inode->buf;
+                       buf->st_mode = (buf->st_mode & S_IFMT) |
+                               pseudo_ent->dev->mode;
+                       buf->st_uid = pseudo_ent->dev->uid;
+                       buf->st_gid = pseudo_ent->dev->gid;
+                       continue;
+               }
+
+               if(dir_ent) {
+                       if(dir_ent->inode->root_entry) {
+                               ERROR_START("Pseudo file \"%s\" is a "
+                                       "pre-existing file in the filesystem "
+                                       "being appended to.",
+                                       pseudo_ent->pathname);
+                               ERROR_EXIT("  Ignoring.\n");
+                       } else {
+                               ERROR_START("Pseudo file \"%s\" exists in "
+                                       "source filesystem \"%s\".",
+                                       pseudo_ent->pathname,
+                                       pathname(dir_ent));
+                               ERROR_EXIT("\nIgnoring, exclude it (-e/-ef) to "
+                                       "override.\n");
+                       }
+                       continue;
+               }
+
+               memset(&buf, 0, sizeof(buf));
+               buf.st_mode = pseudo_ent->dev->mode;
+               buf.st_uid = pseudo_ent->dev->uid;
+               buf.st_gid = pseudo_ent->dev->gid;
+               buf.st_rdev = makedev(pseudo_ent->dev->major,
+                       pseudo_ent->dev->minor);
+               buf.st_mtime = time(NULL);
+               buf.st_ino = pseudo_ino ++;
+
+               if(pseudo_ent->dev->type == 'd') {
+                       struct dir_ent *dir_ent =
+                               create_dir_entry(pseudo_ent->name, NULL,
+                                               pseudo_ent->pathname, dir);
+                       char *subpath = subpathname(dir_ent);
+                       struct dir_info *sub_dir = scan1_opendir("", subpath,
+                                               dir->depth + 1);
+                       if(sub_dir == NULL) {
+                               ERROR_START("Could not create pseudo directory "
+                                       "\"%s\"", pseudo_ent->pathname);
+                               ERROR_EXIT(", skipping...\n");
+                               pseudo_ino --;
+                               continue;
+                       }
+                       dir_scan2(sub_dir, pseudo_ent->pseudo);
+                       dir->directory_count ++;
+                       add_dir_entry(dir_ent, sub_dir,
+                               lookup_inode2(&buf, PSEUDO_FILE_OTHER, 0));
+               } else if(pseudo_ent->dev->type == 'f') {
+                       add_dir_entry2(pseudo_ent->name, NULL,
+                               pseudo_ent->pathname, NULL,
+                               lookup_inode2(&buf, PSEUDO_FILE_PROCESS,
+                               pseudo_ent->dev->pseudo_id), dir);
+               } else if(pseudo_ent->dev->type == 's') {
+                       add_dir_entry2(pseudo_ent->name, NULL,
+                               pseudo_ent->pathname, NULL,
+                               lookup_inode3(&buf, PSEUDO_FILE_OTHER, 0,
+                               pseudo_ent->dev->symlink,
+                               strlen(pseudo_ent->dev->symlink) + 1), dir);
+               } else {
+                       add_dir_entry2(pseudo_ent->name, NULL,
+                               pseudo_ent->pathname, NULL,
+                               lookup_inode2(&buf, PSEUDO_FILE_OTHER, 0), dir);
+               }
+       }
+}
+
+
+/*
+ * dir_scan3 routines...
+ * This processes the move action
+ */
+void dir_scan3(struct dir_info *dir)
+{
+       struct dir_ent *dir_ent = NULL;
+
+       while((dir_ent = scan2_readdir(dir, dir_ent)) != NULL) {
+
+               eval_move_actions(root_dir, dir_ent);
+
+               if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR)
+                       dir_scan3(dir_ent->dir);
+       }
+}
+
+
+/*
+ * dir_scan4 routines...
+ * This processes the prune action.  This action is designed to do fine
+ * grained tuning of the in-core directory structure after the exclude,
+ * move and pseudo actions have been performed.  This allows complex
+ * tests to be performed which are impossible at exclude time (i.e.
+ * tests which rely on the in-core directory structure)
+ */
+void free_dir(struct dir_info *dir)
+{
+       struct dir_ent *dir_ent = dir->list;
+
+       while(dir_ent) {
+               struct dir_ent *tmp = dir_ent;
+
+               if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR)
+                       free_dir(dir_ent->dir);
+
+               dir_ent = dir_ent->next;
+               free_dir_entry(tmp);
+       }
+
+       free(dir->pathname);
+       free(dir->subpath);
+       free(dir);
+}
+       
+
+void dir_scan4(struct dir_info *dir)
+{
+       struct dir_ent *dir_ent = dir->list, *prev = NULL;
+
+       while(dir_ent) {
+               if(dir_ent->inode->root_entry) {
+                       prev = dir_ent;
+                       dir_ent = dir_ent->next;
+                       continue;
+               }
+
+               if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR)
+                       dir_scan4(dir_ent->dir);
+
+               if(eval_prune_actions(root_dir, dir_ent)) {
+                       struct dir_ent *tmp = dir_ent;
+
+                       if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR) {
+                               free_dir(dir_ent->dir);
+                               dir->directory_count --;
+                       }
+
+                       dir->count --;
+
+                       /* remove dir_ent from list */
+                       dir_ent = dir_ent->next;
+                       if(prev)
+                               prev->next = dir_ent;
+                       else
+                               dir->list = dir_ent;
+                       
+                       /* free it */
+                       free_dir_entry(tmp);
+
+                       add_excluded(dir);
+                       continue;
+               }
+
+               prev = dir_ent;
+               dir_ent = dir_ent->next;
+       }
+}
+
+
+/*
+ * dir_scan5 routines...
+ * This processes the empty action.  This action has to be processed after
+ * all other actions because the previous exclude and move actions and the
+ * pseudo actions affect whether a directory is empty
+ */
+void dir_scan5(struct dir_info *dir)
+{
+       struct dir_ent *dir_ent = dir->list, *prev = NULL;
+
+       while(dir_ent) {
+               if(dir_ent->inode->root_entry) {
+                       prev = dir_ent;
+                       dir_ent = dir_ent->next;
+                       continue;
+               }
+
+               if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR) {
+                       dir_scan5(dir_ent->dir);
+
+                       if(eval_empty_actions(root_dir, dir_ent)) {
+                               struct dir_ent *tmp = dir_ent;
+
+                               /*
+                                * delete sub-directory, this is by definition
+                                * empty
+                                */
+                               free(dir_ent->dir->pathname);
+                               free(dir_ent->dir->subpath);
+                               free(dir_ent->dir);
+
+                               /* remove dir_ent from list */
+                               dir_ent = dir_ent->next;
+                               if(prev)
+                                       prev->next = dir_ent;
+                               else
+                                       dir->list = dir_ent;
+                       
+                               /* free it */
+                               free_dir_entry(tmp);
+
+                               /* update counts */
+                               dir->directory_count --;
+                               dir->count --;
+                               add_excluded(dir);
+                               continue;
+                       }
+               }
+
+               prev = dir_ent;
+               dir_ent = dir_ent->next;
+       }
+}
+
+
+/*
+ * dir_scan6 routines...
+ * This sorts every directory and computes the inode numbers
+ */
+
+/*
+ * Bottom up linked list merge sort.
+ *
+ * Qsort and other O(n log n) algorithms work well with arrays but not
+ * linked lists.  Merge sort another O(n log n) sort algorithm on the other hand
+ * is not ideal for arrays (as it needs an additonal n storage locations
+ * as sorting is not done in place), but it is ideal for linked lists because
+ * it doesn't require any extra storage,
+ */ 
+void sort_directory(struct dir_info *dir)
+{
+       struct dir_ent *cur, *l1, *l2, *next;
+       int len1, len2, stride = 1;
+
+       if(dir->list == NULL || dir->count < 2)
+               return;
+
+       /*
+        * We can consider our linked-list to be made up of stride length
+        * sublists.  Eacn iteration around this loop merges adjacent
+        * stride length sublists into larger 2*stride sublists.  We stop
+        * when stride becomes equal to the entire list.
+        *
+        * Initially stride = 1 (by definition a sublist of 1 is sorted), and
+        * these 1 element sublists are merged into 2 element sublists,  which
+        * are then merged into 4 element sublists and so on.
+        */
+       do {
+               l2 = dir->list; /* head of current linked list */
+               cur = NULL; /* empty output list */
+
+               /*
+                * Iterate through the linked list, merging adjacent sublists.
+                * On each interation l2 points to the next sublist pair to be
+                * merged (if there's only one sublist left this is simply added
+                * to the output list)
+                */
+               while(l2) {
+                       l1 = l2;
+                       for(len1 = 0; l2 && len1 < stride; len1 ++, l2 = l2->next);
+                       len2 = stride;
+
+                       /*
+                        * l1 points to first sublist.
+                        * l2 points to second sublist.
+                        * Merge them onto the output list
+                        */
+                       while(len1 && l2 && len2) {
+                               if(strcmp(l1->name, l2->name) <= 0) {
+                                       next = l1;
+                                       l1 = l1->next;
+                                       len1 --;
+                               } else {
+                                       next = l2;
+                                       l2 = l2->next;
+                                       len2 --;
+                               }
+
+                               if(cur) {
+                                       cur->next = next;
+                                       cur = next;
+                               } else
+                                       dir->list = cur = next;
+                       }
+                       /*
+                        * One sublist is now empty, copy the other one onto the
+                        * output list
+                        */
+                       for(; len1; len1 --, l1 = l1->next) {
+                               if(cur) {
+                                       cur->next = l1;
+                                       cur = l1;
+                               } else
+                                       dir->list = cur = l1;
+                       }
+                       for(; l2 && len2; len2 --, l2 = l2->next) {
+                               if(cur) {
+                                       cur->next = l2;
+                                       cur = l2;
+                               } else
+                                       dir->list = cur = l2;
+                       }
+               }
+               cur->next = NULL;
+               stride = stride << 1;
+       } while(stride < dir->count);
+}
+
+
+void dir_scan6(struct dir_info *dir)
+{
+       struct dir_ent *dir_ent;
+       unsigned int byte_count = 0;
+
+       sort_directory(dir);
+
+       for(dir_ent = dir->list; dir_ent; dir_ent = dir_ent->next) {
+               byte_count += strlen(dir_ent->name) +
+                       sizeof(struct squashfs_dir_entry);
+
+               if(dir_ent->inode->root_entry)
+                       continue;
+
+               alloc_inode_no(dir_ent->inode, 0);
+
+               if((dir_ent->inode->buf.st_mode & S_IFMT) == S_IFDIR)
+                       dir_scan6(dir_ent->dir);
+       }
+
+       if((dir->count < 257 && byte_count < SQUASHFS_METADATA_SIZE))
+               dir->dir_is_ldir = FALSE;
+}
+
+
+/*
+ * dir_scan6 routines...
+ * This generates the filesystem metadata and writes it out to the destination
+ */
+void scan7_init_dir(struct directory *dir)
+{
+       dir->buff = malloc(SQUASHFS_METADATA_SIZE);
+       if(dir->buff == NULL)
+               MEM_ERROR();
+
+       dir->size = SQUASHFS_METADATA_SIZE;
+       dir->p = dir->index_count_p = dir->buff;
+       dir->entry_count = 256;
+       dir->entry_count_p = NULL;
+       dir->index = NULL;
+       dir->i_count = dir->i_size = 0;
+}
+
+
+struct dir_ent *scan7_readdir(struct directory *dir, struct dir_info *dir_info,
+       struct dir_ent *dir_ent)
+{
+       if (dir_ent == NULL)
+               dir_ent = dir_info->list;
+       else
+               dir_ent = dir_ent->next;
+
+       for(; dir_ent && dir_ent->inode->root_entry; dir_ent = dir_ent->next)
+               add_dir(dir_ent->inode->inode, dir_ent->inode->inode_number,
+                       dir_ent->name, dir_ent->inode->type, dir);
+
+       return dir_ent; 
+}
+
+
+void scan7_freedir(struct directory *dir)
+{
+       if(dir->index)
+               free(dir->index);
+       free(dir->buff);
+}
+
+
+void dir_scan7(squashfs_inode *inode, struct dir_info *dir_info)
+{
+       int squashfs_type;
+       int duplicate_file;
+       struct directory dir;
+       struct dir_ent *dir_ent = NULL;
+       
+       scan7_init_dir(&dir);
+       
+       while((dir_ent = scan7_readdir(&dir, dir_info, dir_ent)) != NULL) {
+               struct stat *buf = &dir_ent->inode->buf;
+
+               update_info(dir_ent);
+
+               if(dir_ent->inode->inode == SQUASHFS_INVALID_BLK) {
+                       switch(buf->st_mode & S_IFMT) {
+                               case S_IFREG:
+                                       squashfs_type = SQUASHFS_FILE_TYPE;
+                                       write_file(inode, dir_ent,
+                                               &duplicate_file);
+                                       INFO("file %s, uncompressed size %lld "
+                                               "bytes %s\n",
+                                               subpathname(dir_ent),
+                                               (long long) buf->st_size,
+                                               duplicate_file ?  "DUPLICATE" :
+                                                "");
+                                       break;
+
+                               case S_IFDIR:
+                                       squashfs_type = SQUASHFS_DIR_TYPE;
+                                       dir_scan7(inode, dir_ent->dir);
+                                       break;
+
+                               case S_IFLNK:
+                                       squashfs_type = SQUASHFS_SYMLINK_TYPE;
+                                       create_inode(inode, NULL, dir_ent,
+                                               squashfs_type, 0, 0, 0, NULL,
+                                               NULL, NULL, 0);
+                                       INFO("symbolic link %s inode 0x%llx\n",
+                                               subpathname(dir_ent), *inode);
+                                       sym_count ++;
+                                       break;
+
+                               case S_IFCHR:
+                                       squashfs_type = SQUASHFS_CHRDEV_TYPE;
+                                       create_inode(inode, NULL, dir_ent,
+                                               squashfs_type, 0, 0, 0, NULL,
+                                               NULL, NULL, 0);
+                                       INFO("character device %s inode 0x%llx"
+                                               "\n", subpathname(dir_ent),
+                                               *inode);
+                                       dev_count ++;
+                                       break;
+
+                               case S_IFBLK:
+                                       squashfs_type = SQUASHFS_BLKDEV_TYPE;
+                                       create_inode(inode, NULL, dir_ent,
+                                               squashfs_type, 0, 0, 0, NULL,
+                                               NULL, NULL, 0);
+                                       INFO("block device %s inode 0x%llx\n",
+                                               subpathname(dir_ent), *inode);
+                                       dev_count ++;
+                                       break;
+
+                               case S_IFIFO:
+                                       squashfs_type = SQUASHFS_FIFO_TYPE;
+                                       create_inode(inode, NULL, dir_ent,
+                                               squashfs_type, 0, 0, 0, NULL,
+                                               NULL, NULL, 0);
+                                       INFO("fifo %s inode 0x%llx\n",
+                                               subpathname(dir_ent), *inode);
+                                       fifo_count ++;
+                                       break;
+
+                               case S_IFSOCK:
+                                       squashfs_type = SQUASHFS_SOCKET_TYPE;
+                                       create_inode(inode, NULL, dir_ent,
+                                               squashfs_type, 0, 0, 0, NULL,
+                                               NULL, NULL, 0);
+                                       INFO("unix domain socket %s inode "
+                                               "0x%llx\n",
+                                               subpathname(dir_ent), *inode);
+                                       sock_count ++;
+                                       break;
+
+                               default:
+                                       BAD_ERROR("%s unrecognised file type, "
+                                               "mode is %x\n",
+                                               subpathname(dir_ent),
+                                               buf->st_mode);
+                       }
+                       dir_ent->inode->inode = *inode;
+                       dir_ent->inode->type = squashfs_type;
+                } else {
+                       *inode = dir_ent->inode->inode;
+                       squashfs_type = dir_ent->inode->type;
+                       switch(squashfs_type) {
+                               case SQUASHFS_FILE_TYPE:
+                                       if(!sorted)
+                                               INFO("file %s, uncompressed "
+                                                       "size %lld bytes LINK"
+                                                       "\n",
+                                                       subpathname(dir_ent),
+                                                       (long long)
+                                                       buf->st_size);
+                                       break;
+                               case SQUASHFS_SYMLINK_TYPE:
+                                       INFO("symbolic link %s inode 0x%llx "
+                                               "LINK\n", subpathname(dir_ent),
+                                                *inode);
+                                       break;
+                               case SQUASHFS_CHRDEV_TYPE:
+                                       INFO("character device %s inode 0x%llx "
+                                               "LINK\n", subpathname(dir_ent),
+                                               *inode);
+                                       break;
+                               case SQUASHFS_BLKDEV_TYPE:
+                                       INFO("block device %s inode 0x%llx "
+                                               "LINK\n", subpathname(dir_ent),
+                                               *inode);
+                                       break;
+                               case SQUASHFS_FIFO_TYPE:
+                                       INFO("fifo %s inode 0x%llx LINK\n",
+                                               subpathname(dir_ent), *inode);
+                                       break;
+                               case SQUASHFS_SOCKET_TYPE:
+                                       INFO("unix domain socket %s inode "
+                                               "0x%llx LINK\n",
+                                               subpathname(dir_ent), *inode);
+                                       break;
+                       }
+               }
+               
+               add_dir(*inode, get_inode_no(dir_ent->inode), dir_ent->name,
+                       squashfs_type, &dir);
+       }
+
+       write_dir(inode, dir_info, &dir);
+       INFO("directory %s inode 0x%llx\n", subpathname(dir_info->dir_ent),
+               *inode);
+
+       scan7_freedir(&dir);
+}
+
+
+unsigned int slog(unsigned int block)
+{
+       int i;
+
+       for(i = 12; i <= 20; i++)
+               if(block == (1 << i))
+                       return i;
+       return 0;
+}
+
+
+int old_excluded(char *filename, struct stat *buf)
+{
+       int i;
+
+       for(i = 0; i < exclude; i++)
+               if((exclude_paths[i].st_dev == buf->st_dev) &&
+                               (exclude_paths[i].st_ino == buf->st_ino))
+                       return TRUE;
+       return FALSE;
+}
+
+
+#define ADD_ENTRY(buf) \
+       if(exclude % EXCLUDE_SIZE == 0) { \
+               exclude_paths = realloc(exclude_paths, (exclude + EXCLUDE_SIZE) \
+                       * sizeof(struct exclude_info)); \
+               if(exclude_paths == NULL) \
+                       MEM_ERROR(); \
+       } \
+       exclude_paths[exclude].st_dev = buf.st_dev; \
+       exclude_paths[exclude++].st_ino = buf.st_ino;
+int old_add_exclude(char *path)
+{
+       int i;
+       char *filename;
+       struct stat buf;
+
+       if(path[0] == '/' || strncmp(path, "./", 2) == 0 ||
+                       strncmp(path, "../", 3) == 0) {
+               if(lstat(path, &buf) == -1) {
+                       ERROR_START("Cannot stat exclude dir/file %s because "
+                               "%s", path, strerror(errno));
+                       ERROR_EXIT(", ignoring\n");
+                       return TRUE;
+               }
+               ADD_ENTRY(buf);
+               return TRUE;
+       }
+
+       for(i = 0; i < source; i++) {
+               int res = asprintf(&filename, "%s/%s", source_path[i], path);
+               if(res == -1)
+                       BAD_ERROR("asprintf failed in old_add_exclude\n");
+               if(lstat(filename, &buf) == -1) {
+                       if(!(errno == ENOENT || errno == ENOTDIR)) {
+                               ERROR_START("Cannot stat exclude dir/file %s "
+                                       "because %s", filename, strerror(errno));
+                               ERROR_EXIT(", ignoring\n");
+                       }
+                       free(filename);
+                       continue;
+               }
+               free(filename);
+               ADD_ENTRY(buf);
+       }
+       return TRUE;
+}
+
+
+void add_old_root_entry(char *name, squashfs_inode inode, int inode_number,
+       int type)
+{
+       old_root_entry = realloc(old_root_entry,
+               sizeof(struct old_root_entry_info) * (old_root_entries + 1));
+       if(old_root_entry == NULL)
+               MEM_ERROR();
+
+       old_root_entry[old_root_entries].name = strdup(name);
+       old_root_entry[old_root_entries].inode.inode = inode;
+       old_root_entry[old_root_entries].inode.inode_number = inode_number;
+       old_root_entry[old_root_entries].inode.type = type;
+       old_root_entry[old_root_entries++].inode.root_entry = TRUE;
+}
+
+
+void initialise_threads(int readq, int fragq, int bwriteq, int fwriteq,
+       int freelst, char *destination_file)
+{
+       int i;
+       sigset_t sigmask, old_mask;
+       int total_mem = readq;
+       int reader_size;
+       int fragment_size;
+       int fwriter_size;
+       /*
+        * bwriter_size is global because it is needed in
+        * write_file_blocks_dup()
+        */
+
+       /*
+        * Never allow the total size of the queues to be larger than
+        * physical memory
+        *
+        * When adding together the possibly user supplied values, make
+        * sure they've not been deliberately contrived to overflow an int
+        */
+       if(add_overflow(total_mem, fragq))
+               BAD_ERROR("Queue sizes rediculously too large\n");
+       total_mem += fragq;
+       if(add_overflow(total_mem, bwriteq))
+               BAD_ERROR("Queue sizes rediculously too large\n");
+       total_mem += bwriteq;
+       if(add_overflow(total_mem, fwriteq))
+               BAD_ERROR("Queue sizes rediculously too large\n");
+       total_mem += fwriteq;
+
+       check_usable_phys_mem(total_mem);
+
+       /*
+        * convert from queue size in Mbytes to queue size in
+        * blocks.
+        *
+        * This isn't going to overflow an int unless there exists
+        * systems with more than 8 Petabytes of RAM!
+        */
+       reader_size = readq << (20 - block_log);
+       fragment_size = fragq << (20 - block_log);
+       bwriter_size = bwriteq << (20 - block_log);
+       fwriter_size = fwriteq << (20 - block_log);
+
+       /*
+        * setup signal handlers for the main thread, these cleanup
+        * deleting the destination file, if appending the
+        * handlers for SIGTERM and SIGINT will be replaced with handlers
+        * allowing the user to press ^C twice to restore the existing
+        * filesystem.
+        *
+        * SIGUSR1 is an internal signal, which is used by the sub-threads
+        * to tell the main thread to terminate, deleting the destination file,
+        * or if necessary restoring the filesystem on appending
+        */
+       signal(SIGTERM, sighandler);
+       signal(SIGINT, sighandler);
+       signal(SIGUSR1, sighandler);
+
+       /* block SIGQUIT and SIGHUP, these are handled by the info thread */
+       sigemptyset(&sigmask);
+       sigaddset(&sigmask, SIGQUIT);
+       sigaddset(&sigmask, SIGHUP);
+       if(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) != 0)
+               BAD_ERROR("Failed to set signal mask in intialise_threads\n");
+
+       /*
+        * temporarily block these signals, so the created sub-threads
+        * will ignore them, ensuring the main thread handles them
+        */
+       sigemptyset(&sigmask);
+       sigaddset(&sigmask, SIGINT);
+       sigaddset(&sigmask, SIGTERM);
+       sigaddset(&sigmask, SIGUSR1);
+       if(pthread_sigmask(SIG_BLOCK, &sigmask, &old_mask) != 0)
+               BAD_ERROR("Failed to set signal mask in intialise_threads\n");
+
+       if(processors == -1) {
+#ifndef linux
+               int mib[2];
+               size_t len = sizeof(processors);
+
+               mib[0] = CTL_HW;
+#ifdef HW_AVAILCPU
+               mib[1] = HW_AVAILCPU;
+#else
+               mib[1] = HW_NCPU;
+#endif
+
+               if(sysctl(mib, 2, &processors, &len, NULL, 0) == -1) {
+                       ERROR_START("Failed to get number of available "
+                               "processors.");
+                       ERROR_EXIT("  Defaulting to 1\n");
+                       processors = 1;
+               }
+#else
+               processors = sysconf(_SC_NPROCESSORS_ONLN);
+#endif
+       }
+
+       if(multiply_overflow(processors, 3) ||
+                       multiply_overflow(processors * 3, sizeof(pthread_t)))
+               BAD_ERROR("Processors too large\n");
+
+       deflator_thread = malloc(processors * 3 * sizeof(pthread_t));
+       if(deflator_thread == NULL)
+               MEM_ERROR();
+
+       frag_deflator_thread = &deflator_thread[processors];
+       frag_thread = &frag_deflator_thread[processors];
+
+       to_reader = queue_init(1);
+       to_deflate = queue_init(reader_size);
+       to_process_frag = queue_init(reader_size);
+       to_writer = queue_init(bwriter_size + fwriter_size);
+       from_writer = queue_init(1);
+       to_frag = queue_init(fragment_size);
+       to_main = seq_queue_init();
+       if(reproducible)
+               to_order = seq_queue_init();
+       else
+               locked_fragment = queue_init(fragment_size);
+       reader_buffer = cache_init(block_size, reader_size, 0, 0);
+       bwriter_buffer = cache_init(block_size, bwriter_size, 1, freelst);
+       fwriter_buffer = cache_init(block_size, fwriter_size, 1, freelst);
+       fragment_buffer = cache_init(block_size, fragment_size, 1, 0);
+       reserve_cache = cache_init(block_size, processors + 1, 1, 0);
+       pthread_create(&reader_thread, NULL, reader, NULL);
+       pthread_create(&writer_thread, NULL, writer, NULL);
+       init_progress_bar();
+       init_info();
+
+       for(i = 0; i < processors; i++) {
+               if(pthread_create(&deflator_thread[i], NULL, deflator, NULL))
+                       BAD_ERROR("Failed to create thread\n");
+               if(pthread_create(&frag_deflator_thread[i], NULL, reproducible ?
+                               frag_order_deflator : frag_deflator, NULL) != 0)
+                       BAD_ERROR("Failed to create thread\n");
+               if(pthread_create(&frag_thread[i], NULL, frag_thrd,
+                               (void *) destination_file) != 0)
+                       BAD_ERROR("Failed to create thread\n");
+       }
+
+       main_thread = pthread_self();
+
+       if(reproducible)
+               pthread_create(&order_thread, NULL, frag_orderer, NULL);
+
+       if(!quiet)
+               printf("Parallel mksquashfs: Using %d processor%s\n", processors,
+                       processors == 1 ? "" : "s");
+
+       /* Restore the signal mask for the main thread */
+       if(pthread_sigmask(SIG_SETMASK, &old_mask, NULL) != 0)
+               BAD_ERROR("Failed to set signal mask in intialise_threads\n");
+}
+
+
+long long write_inode_lookup_table()
+{
+       int i, inode_number, lookup_bytes = SQUASHFS_LOOKUP_BYTES(inode_count);
+       void *it;
+
+       if(inode_count == sinode_count)
+               goto skip_inode_hash_table;
+
+       it = realloc(inode_lookup_table, lookup_bytes);
+       if(it == NULL)
+               MEM_ERROR();
+       inode_lookup_table = it;
+
+       for(i = 0; i < INODE_HASH_SIZE; i ++) {
+               struct inode_info *inode;
+
+               for(inode = inode_info[i]; inode; inode = inode->next) {
+
+                       inode_number = get_inode_no(inode);
+
+                       /* The empty action will produce orphaned inode
+                        * entries in the inode_info[] table.  These
+                        * entries because they are orphaned will not be
+                        * allocated an inode number in dir_scan5(), so
+                        * skip any entries with the default dummy inode
+                        * number of 0 */
+                       if(inode_number == 0)
+                               continue;
+
+                       SQUASHFS_SWAP_LONG_LONGS(&inode->inode,
+                               &inode_lookup_table[inode_number - 1], 1);
+
+               }
+       }
+
+skip_inode_hash_table:
+       return generic_write_table(lookup_bytes, inode_lookup_table, 0, NULL,
+               noI);
+}
+
+
+char *get_component(char *target, char **targname)
+{
+       char *start;
+
+       while(*target == '/')
+               target ++;
+
+       start = target;
+       while(*target != '/' && *target != '\0')
+               target ++;
+
+       *targname = strndup(start, target - start);
+
+       while(*target == '/')
+               target ++;
+
+       return target;
+}
+
+
+void free_path(struct pathname *paths)
+{
+       int i;
+
+       for(i = 0; i < paths->names; i++) {
+               if(paths->name[i].paths)
+                       free_path(paths->name[i].paths);
+               free(paths->name[i].name);
+               if(paths->name[i].preg) {
+                       regfree(paths->name[i].preg);
+                       free(paths->name[i].preg);
+               }
+       }
+
+       free(paths);
+}
+
+
+struct pathname *add_path(struct pathname *paths, char *target, char *alltarget)
+{
+       char *targname;
+       int i, error;
+
+       target = get_component(target, &targname);
+
+       if(paths == NULL) {
+               paths = malloc(sizeof(struct pathname));
+               if(paths == NULL)
+                       MEM_ERROR();
+
+               paths->names = 0;
+               paths->name = NULL;
+       }
+
+       for(i = 0; i < paths->names; i++)
+               if(strcmp(paths->name[i].name, targname) == 0)
+                       break;
+
+       if(i == paths->names) {
+               /* allocate new name entry */
+               paths->names ++;
+               paths->name = realloc(paths->name, (i + 1) *
+                       sizeof(struct path_entry));
+               if(paths->name == NULL)
+                       MEM_ERROR();
+               paths->name[i].name = targname;
+               paths->name[i].paths = NULL;
+               if(use_regex) {
+                       paths->name[i].preg = malloc(sizeof(regex_t));
+                       if(paths->name[i].preg == NULL)
+                               MEM_ERROR();
+                       error = regcomp(paths->name[i].preg, targname,
+                               REG_EXTENDED|REG_NOSUB);
+                       if(error) {
+                               char str[1024]; /* overflow safe */
+
+                               regerror(error, paths->name[i].preg, str, 1024);
+                               BAD_ERROR("invalid regex %s in export %s, "
+                                       "because %s\n", targname, alltarget,
+                                       str);
+                       }
+               } else
+                       paths->name[i].preg = NULL;
+
+               if(target[0] == '\0')
+                       /* at leaf pathname component */
+                       paths->name[i].paths = NULL;
+               else
+                       /* recurse adding child components */
+                       paths->name[i].paths = add_path(NULL, target,
+                               alltarget);
+       } else {
+               /* existing matching entry */
+               free(targname);
+
+               if(paths->name[i].paths == NULL) {
+                       /* No sub-directory which means this is the leaf
+                        * component of a pre-existing exclude which subsumes
+                        * the exclude currently being added, in which case stop
+                        * adding components */
+               } else if(target[0] == '\0') {
+                       /* at leaf pathname component and child components exist
+                        * from more specific excludes, delete as they're
+                        * subsumed by this exclude */
+                       free_path(paths->name[i].paths);
+                       paths->name[i].paths = NULL;
+               } else
+                       /* recurse adding child components */
+                       add_path(paths->name[i].paths, target, alltarget);
+       }
+
+       return paths;
+}
+
+
+void add_exclude(char *target)
+{
+
+       if(target[0] == '/' || strncmp(target, "./", 2) == 0 ||
+                       strncmp(target, "../", 3) == 0)
+               BAD_ERROR("/, ./ and ../ prefixed excludes not supported with "
+                       "-wildcards or -regex options\n");      
+       else if(strncmp(target, "... ", 4) == 0)
+               stickypath = add_path(stickypath, target + 4, target + 4);
+       else    
+               path = add_path(path, target, target);
+}
+
+
+void display_path(int depth, struct pathname *paths)
+{
+       int i, n;
+
+       if(paths == NULL)
+               return;
+
+       for(i = 0; i < paths->names; i++) {
+               for(n = 0; n < depth; n++)
+                       printf("\t");
+               printf("%d: %s\n", depth, paths->name[i].name);
+               display_path(depth + 1, paths->name[i].paths);
+       }
+}
+
+
+void display_path2(struct pathname *paths, char *string)
+{
+       int i;
+       char *path;
+
+       if(paths == NULL) {
+               printf("%s\n", string);
+               return;
+       }
+
+       for(i = 0; i < paths->names; i++) {
+               int res = asprintf(&path, "%s/%s", string, paths->name[i].name);
+               if(res == -1)
+                       BAD_ERROR("asprintf failed in display_path2\n");
+               display_path2(paths->name[i].paths, path);
+               free(path);
+       }
+}
+
+
+struct pathnames *add_subdir(struct pathnames *paths, struct pathname *path)
+{
+       int count = paths == NULL ? 0 : paths->count;
+
+       if(count % PATHS_ALLOC_SIZE == 0) {
+               paths = realloc(paths, sizeof(struct pathnames) +
+                       (count + PATHS_ALLOC_SIZE) * sizeof(struct pathname *));
+               if(paths == NULL)
+                       MEM_ERROR();
+       }
+
+       paths->path[count] = path;
+       paths->count = count  + 1;
+       return paths;
+}
+
+
+int excluded_match(char *name, struct pathname *path, struct pathnames **new)
+{
+       int i;
+
+       for(i = 0; i < path->names; i++) {
+               int match = use_regex ?
+                       regexec(path->name[i].preg, name, (size_t) 0,
+                                       NULL, 0) == 0 :
+                       fnmatch(path->name[i].name, name,
+                               FNM_PATHNAME|FNM_PERIOD|FNM_EXTMATCH) == 0;
+
+               if(match) {
+                        if(path->name[i].paths == NULL || new == NULL)
+                               /* match on a leaf component, any subdirectories
+                               * in the filesystem should be excluded */
+                               return TRUE;
+                       else
+                               /* match on a non-leaf component, add any
+                                * subdirectories to the new set of
+                                * subdirectories to scan for this name */
+                               *new = add_subdir(*new, path->name[i].paths);
+               }
+       }
+
+       return FALSE;
+}
+
+
+int excluded(char *name, struct pathnames *paths, struct pathnames **new)
+{
+       int n;
+               
+       if(stickypath && excluded_match(name, stickypath, NULL))
+               return TRUE;
+
+       for(n = 0; paths && n < paths->count; n++) {
+               int res = excluded_match(name, paths->path[n], new);
+               if(res) {
+                       free(*new);
+                       *new = NULL;
+                       return TRUE;
+               }
+       }
+
+       /*
+        * Either:
+        * -  no matching names found, return empty new search set, or
+        * -  one or more matches with sub-directories found (no leaf matches),
+        *    in which case return new search set.
+        *
+        * In either case return FALSE as we don't want to exclude this entry
+        */
+       return FALSE;
+}
+
+
+void process_exclude_file(char *argv)
+{
+       FILE *fd;
+       char buffer[MAX_LINE + 1]; /* overflow safe */
+       char *filename;
+
+       fd = fopen(argv, "r");
+       if(fd == NULL)
+               BAD_ERROR("Failed to open exclude file \"%s\" because %s\n",
+                       argv, strerror(errno));
+
+       while(fgets(filename = buffer, MAX_LINE + 1, fd) != NULL) {
+               int len = strlen(filename);
+
+               if(len == MAX_LINE && filename[len - 1] != '\n')
+                       /* line too large */
+                       BAD_ERROR("Line too long when reading "
+                               "exclude file \"%s\", larger than %d "
+                               "bytes\n", argv, MAX_LINE);
+
+               /*
+                * Remove '\n' terminator if it exists (the last line
+                * in the file may not be '\n' terminated)
+                */
+               if(len && filename[len - 1] == '\n')
+                       filename[len - 1] = '\0';
+
+               /* Skip any leading whitespace */
+               while(isspace(*filename))
+                       filename ++;
+
+               /* if comment line, skip */
+               if(*filename == '#')
+                       continue;
+
+               /*
+                * check for initial backslash, to accommodate
+                * filenames with leading space or leading # character
+                */
+               if(*filename == '\\')
+                       filename ++;
+
+               /* if line is now empty after skipping characters, skip it */
+               if(*filename == '\0')
+                       continue;
+
+               if(old_exclude)
+                       old_add_exclude(filename);
+               else
+                       add_exclude(filename);
+       }
+
+       if(ferror(fd))
+               BAD_ERROR("Reading exclude file \"%s\" failed because %s\n",
+                       argv, strerror(errno));
+
+       fclose(fd);
+}
+
+
+#define RECOVER_ID "Squashfs recovery file v1.0\n"
+#define RECOVER_ID_SIZE 28
+
+void write_recovery_data(struct squashfs_super_block *sBlk)
+{
+       int res, recoverfd, bytes = sBlk->bytes_used - sBlk->inode_table_start;
+       pid_t pid = getpid();
+       char *metadata;
+       char header[] = RECOVER_ID;
+
+       if(recover == FALSE) {
+               printf("No recovery data option specified.\n");
+               printf("Skipping saving recovery file.\n\n");
+               return;
+       }
+
+       metadata = malloc(bytes);
+       if(metadata == NULL)
+               MEM_ERROR();
+
+       res = read_fs_bytes(fd, sBlk->inode_table_start, bytes, metadata);
+       if(res == 0) {
+               ERROR("Failed to read append filesystem metadata\n");
+               BAD_ERROR("Filesystem corrupted?\n");
+       }
+
+       res = asprintf(&recovery_file, "squashfs_recovery_%s_%d",
+               getbase(destination_file), pid);
+       if(res == -1)
+               MEM_ERROR();
+
+       recoverfd = open(recovery_file, O_CREAT | O_TRUNC | O_RDWR, S_IRWXU);
+       if(recoverfd == -1)
+               BAD_ERROR("Failed to create recovery file, because %s.  "
+                       "Aborting\n", strerror(errno));
+               
+       if(write_bytes(recoverfd, header, RECOVER_ID_SIZE) == -1)
+               BAD_ERROR("Failed to write recovery file, because %s\n",
+                       strerror(errno));
+
+       if(write_bytes(recoverfd, sBlk, sizeof(struct squashfs_super_block)) == -1)
+               BAD_ERROR("Failed to write recovery file, because %s\n",
+                       strerror(errno));
+
+       if(write_bytes(recoverfd, metadata, bytes) == -1)
+               BAD_ERROR("Failed to write recovery file, because %s\n",
+                       strerror(errno));
+
+       close(recoverfd);
+       free(metadata);
+       
+       printf("Recovery file \"%s\" written\n", recovery_file);
+       printf("If Mksquashfs aborts abnormally (i.e. power failure), run\n");
+       printf("mksquashfs dummy %s -recover %s\n", destination_file,
+               recovery_file);
+       printf("to restore filesystem\n\n");
+}
+
+
+void read_recovery_data(char *recovery_file, char *destination_file)
+{
+       int fd, recoverfd, bytes;
+       struct squashfs_super_block orig_sBlk, sBlk;
+       char *metadata;
+       int res;
+       struct stat buf;
+       char header[] = RECOVER_ID;
+       char header2[RECOVER_ID_SIZE];
+
+       recoverfd = open(recovery_file, O_RDONLY);
+       if(recoverfd == -1)
+               BAD_ERROR("Failed to open recovery file because %s\n",
+                       strerror(errno));
+
+       if(stat(destination_file, &buf) == -1)
+               BAD_ERROR("Failed to stat destination file, because %s\n",
+                       strerror(errno));
+
+       fd = open(destination_file, O_RDWR);
+       if(fd == -1)
+               BAD_ERROR("Failed to open destination file because %s\n",
+                       strerror(errno));
+
+       res = read_bytes(recoverfd, header2, RECOVER_ID_SIZE);
+       if(res == -1)
+               BAD_ERROR("Failed to read recovery file, because %s\n",
+                       strerror(errno));
+       if(res < RECOVER_ID_SIZE)
+               BAD_ERROR("Recovery file appears to be truncated\n");
+       if(strncmp(header, header2, RECOVER_ID_SIZE) !=0 )
+               BAD_ERROR("Not a recovery file\n");
+
+       res = read_bytes(recoverfd, &sBlk, sizeof(struct squashfs_super_block));
+       if(res == -1)
+               BAD_ERROR("Failed to read recovery file, because %s\n",
+                       strerror(errno));
+       if(res < sizeof(struct squashfs_super_block))
+               BAD_ERROR("Recovery file appears to be truncated\n");
+
+       res = read_fs_bytes(fd, 0, sizeof(struct squashfs_super_block), &orig_sBlk);
+       if(res == 0) {
+               ERROR("Failed to read superblock from output filesystem\n");
+               BAD_ERROR("Output filesystem is empty!\n");
+       }
+
+       if(memcmp(((char *) &sBlk) + 4, ((char *) &orig_sBlk) + 4,
+                       sizeof(struct squashfs_super_block) - 4) != 0)
+               BAD_ERROR("Recovery file and destination file do not seem to "
+                       "match\n");
+
+       bytes = sBlk.bytes_used - sBlk.inode_table_start;
+
+       metadata = malloc(bytes);
+       if(metadata == NULL)
+               MEM_ERROR();
+
+       res = read_bytes(recoverfd, metadata, bytes);
+       if(res == -1)
+               BAD_ERROR("Failed to read recovery file, because %s\n",
+                       strerror(errno));
+       if(res < bytes)
+               BAD_ERROR("Recovery file appears to be truncated\n");
+
+       write_destination(fd, 0, sizeof(struct squashfs_super_block), &sBlk);
+
+       write_destination(fd, sBlk.inode_table_start, bytes, metadata);
+
+       close(recoverfd);
+       close(fd);
+
+       printf("Successfully wrote recovery file \"%s\".  Exiting\n",
+               recovery_file);
+       
+       exit(0);
+}
+
+
+void write_filesystem_tables(struct squashfs_super_block *sBlk, int nopad)
+{
+       int i;
+
+       sBlk->fragments = fragments;
+       sBlk->no_ids = id_count;
+       sBlk->inode_table_start = write_inodes();
+       sBlk->directory_table_start = write_directories();
+       sBlk->fragment_table_start = write_fragment_table();
+       sBlk->lookup_table_start = exportable ? write_inode_lookup_table() :
+               SQUASHFS_INVALID_BLK;
+       sBlk->id_table_start = write_id_table();
+       sBlk->xattr_id_table_start = write_xattrs();
+
+       TRACE("sBlk->inode_table_start 0x%llx\n", sBlk->inode_table_start);
+       TRACE("sBlk->directory_table_start 0x%llx\n",
+               sBlk->directory_table_start);
+       TRACE("sBlk->fragment_table_start 0x%llx\n", sBlk->fragment_table_start);
+       if(exportable)
+               TRACE("sBlk->lookup_table_start 0x%llx\n",
+                       sBlk->lookup_table_start);
+
+       sBlk->bytes_used = bytes;
+
+       sBlk->compression = comp->id;
+
+       SQUASHFS_INSWAP_SUPER_BLOCK(sBlk); 
+       write_destination(fd, SQUASHFS_START, sizeof(*sBlk), sBlk);
+
+       if(!nopad && (i = bytes & (4096 - 1))) {
+               char temp[4096] = {0};
+               write_destination(fd, bytes, 4096 - i, temp);
+       }
+
+       close(fd);
+
+       if(recovery_file)
+               unlink(recovery_file);
+
+       total_bytes += total_inode_bytes + total_directory_bytes +
+               sizeof(struct squashfs_super_block) + total_xattr_bytes;
+
+       if(quiet)
+               return;
+
+       printf("\n%sSquashfs %d.%d filesystem, %s compressed, data block size"
+               " %d\n", exportable ? "Exportable " : "", SQUASHFS_MAJOR,
+               SQUASHFS_MINOR, comp->name, block_size);
+       printf("\t%s data, %s metadata, %s fragments,\n\t%s xattrs, %s ids\n",
+               noD ? "uncompressed" : "compressed", noI ?  "uncompressed" :
+               "compressed", no_fragments ? "no" : noF ? "uncompressed" :
+               "compressed", no_xattrs ? "no" : noX ? "uncompressed" :
+               "compressed", noI || noId ? "uncompressed" : "compressed");
+       printf("\tduplicates are %sremoved\n", duplicate_checking ? "" :
+               "not ");
+       printf("Filesystem size %.2f Kbytes (%.2f Mbytes)\n", bytes / 1024.0,
+               bytes / (1024.0 * 1024.0));
+       printf("\t%.2f%% of uncompressed filesystem size (%.2f Kbytes)\n",
+               ((float) bytes / total_bytes) * 100.0, total_bytes / 1024.0);
+       printf("Inode table size %d bytes (%.2f Kbytes)\n",
+               inode_bytes, inode_bytes / 1024.0);
+       printf("\t%.2f%% of uncompressed inode table size (%d bytes)\n",
+               ((float) inode_bytes / total_inode_bytes) * 100.0,
+               total_inode_bytes);
+       printf("Directory table size %d bytes (%.2f Kbytes)\n",
+               directory_bytes, directory_bytes / 1024.0);
+       printf("\t%.2f%% of uncompressed directory table size (%d bytes)\n",
+               ((float) directory_bytes / total_directory_bytes) * 100.0,
+               total_directory_bytes);
+       if(total_xattr_bytes) {
+               printf("Xattr table size %d bytes (%.2f Kbytes)\n",
+                       xattr_bytes, xattr_bytes / 1024.0);
+               printf("\t%.2f%% of uncompressed xattr table size (%d bytes)\n",
+                       ((float) xattr_bytes / total_xattr_bytes) * 100.0,
+                       total_xattr_bytes);
+       }
+       if(duplicate_checking)
+               printf("Number of duplicate files found %d\n", file_count -
+                       dup_files);
+       else
+               printf("No duplicate files removed\n");
+       printf("Number of inodes %d\n", inode_count);
+       printf("Number of files %d\n", file_count);
+       if(!no_fragments)
+               printf("Number of fragments %d\n", fragments);
+       printf("Number of symbolic links  %d\n", sym_count);
+       printf("Number of device nodes %d\n", dev_count);
+       printf("Number of fifo nodes %d\n", fifo_count);
+       printf("Number of socket nodes %d\n", sock_count);
+       printf("Number of directories %d\n", dir_count);
+       printf("Number of ids (unique uids + gids) %d\n", id_count);
+       printf("Number of uids %d\n", uid_count);
+
+       for(i = 0; i < id_count; i++) {
+               if(id_table[i]->flags & ISA_UID) {
+                       struct passwd *user = getpwuid(id_table[i]->id);
+                       printf("\t%s (%d)\n", user == NULL ? "unknown" :
+                               user->pw_name, id_table[i]->id);
+               }
+       }
+
+       printf("Number of gids %d\n", guid_count);
+
+       for(i = 0; i < id_count; i++) {
+               if(id_table[i]->flags & ISA_GID) {
+                       struct group *group = getgrgid(id_table[i]->id);
+                       printf("\t%s (%d)\n", group == NULL ? "unknown" :
+                               group->gr_name, id_table[i]->id);
+               }
+       }
+}
+
+
+int _parse_numberll(char *start, long long *res, int size, int base)
+{
+       char *end;
+       long long number;
+
+       errno = 0; /* To distinguish success/failure after call */
+
+       number = strtoll(start, &end, base);
+
+       /*
+        * check for strtoll underflow or overflow in conversion, and other
+        * errors.
+        */
+       if((errno == ERANGE && (number == LLONG_MIN || number == LLONG_MAX)) ||
+                       (errno != 0 && number == 0))
+               return 0;
+
+       /* reject negative numbers as invalid */
+       if(number < 0)
+               return 0;
+
+       if(size) {
+               /*
+                * Check for multiplier and trailing junk.
+                * But first check that a number exists before the
+                * multiplier
+                */
+               if(end == start)
+                       return 0;
+
+               switch(end[0]) {
+               case 'g':
+               case 'G':
+                       if(multiply_overflowll(number, 1073741824))
+                               return 0;
+                       number *= 1073741824;
+
+                       if(end[1] != '\0')
+                               /* trailing junk after multiplier, but
+                                * allow it to be "bytes" */
+                               if(strcmp(end + 1, "bytes"))
+                                       return 0;
+
+                       break;
+               case 'm':
+               case 'M':
+                       if(multiply_overflowll(number, 1048576))
+                               return 0;
+                       number *= 1048576;
+
+                       if(end[1] != '\0')
+                               /* trailing junk after multiplier, but
+                                * allow it to be "bytes" */
+                               if(strcmp(end + 1, "bytes"))
+                                       return 0;
+
+                       break;
+               case 'k':
+               case 'K':
+                       if(multiply_overflowll(number, 1024))
+                               return 0;
+                       number *= 1024;
+
+                       if(end[1] != '\0')
+                               /* trailing junk after multiplier, but
+                                * allow it to be "bytes" */
+                               if(strcmp(end + 1, "bytes"))
+                                       return 0;
+
+                       break;
+               case '\0':
+                       break;
+               default:
+                       /* trailing junk after number */
+                       return 0;
+               }
+       } else if(end[0] != '\0')
+               /* trailing junk after number */
+               return 0;
+
+       *res = number;
+       return 1;
+}
+
+
+int parse_numberll(char *start, long long *res, int size)
+{
+       return _parse_numberll(start, res, size, 10);
+}
+
+
+int parse_number(char *start, int *res, int size)
+{
+       long long number;
+
+       if(!_parse_numberll(start, &number, size, 10))
+               return 0;
+       
+       /* check if long result will overflow signed int */
+       if(number > INT_MAX)
+               return 0;
+
+       *res = (int) number;
+       return 1;
+}
+
+
+int parse_number_unsigned(char *start, unsigned int *res, int size)
+{
+       long long number;
+
+       if(!_parse_numberll(start, &number, size, 10))
+               return 0;
+       
+       /* check if long result will overflow unsigned int */
+       if(number > UINT_MAX)
+               return 0;
+
+       *res = (unsigned int) number;
+       return 1;
+}
+
+
+int parse_num(char *arg, int *res)
+{
+       return parse_number(arg, res, 0);
+}
+
+
+int parse_num_unsigned(char *arg, unsigned int *res)
+{
+       return parse_number_unsigned(arg, res, 0);
+}
+
+
+int parse_mode(char *arg, mode_t *res)
+{
+       long long number;
+
+       if(!_parse_numberll(arg, &number, 0, 8))
+               return 0;
+
+       if(number > 07777)
+               return 0;
+
+       *res = (mode_t) number;
+       return 1;
+}
+
+
+int get_physical_memory()
+{
+       /*
+        * Long longs are used here because with PAE, a 32-bit
+        * machine can have more than 4GB of physical memory
+        *
+        * sysconf(_SC_PHYS_PAGES) relies on /proc being mounted.
+        * If it fails use sysinfo, if that fails return 0
+        */
+       long long num_pages = sysconf(_SC_PHYS_PAGES);
+       long long page_size = sysconf(_SC_PAGESIZE);
+       int phys_mem;
+
+       if(num_pages == -1 || page_size == -1) {
+               struct sysinfo sys;
+               int res = sysinfo(&sys);
+
+               if(res == -1)
+                       return 0;
+
+               num_pages = sys.totalram;
+               page_size = sys.mem_unit;
+       }
+
+       phys_mem = num_pages * page_size >> 20;
+
+       if(phys_mem < SQUASHFS_LOWMEM)
+               BAD_ERROR("Mksquashfs requires more physical memory than is "
+                       "available!\n");
+
+       return phys_mem;
+}
+
+
+void check_usable_phys_mem(int total_mem)
+{
+       /*
+        * We want to allow users to use as much of their physical
+        * memory as they wish.  However, for practical reasons there are
+        * limits which need to be imposed, to protect users from themselves
+        * and to prevent people from using Mksquashfs as a DOS attack by using
+        * all physical memory.   Mksquashfs uses memory to cache data from disk
+        * to optimise performance.  It is pointless to ask it to use more
+        * than 75% of physical memory, as this causes thrashing and it is thus
+        * self-defeating.
+        */
+       int mem = get_physical_memory();
+
+       mem = (mem >> 1) + (mem >> 2); /* 75% */
+                                               
+       if(total_mem > mem && mem) {
+               ERROR("Total memory requested is more than 75%% of physical "
+                                               "memory.\n");
+               ERROR("Mksquashfs uses memory to cache data from disk to "
+                                               "optimise performance.\n");
+               ERROR("It is pointless to ask it to use more than this amount "
+                                               "of memory, as this\n");
+               ERROR("causes thrashing and it is thus self-defeating.\n");
+               BAD_ERROR("Requested memory size too large\n");
+       }
+
+       if(sizeof(void *) == 4 && total_mem > 2048) {
+               /*
+                * If we're running on a kernel with PAE or on a 64-bit kernel,
+                * then the 75% physical memory limit can still easily exceed
+                * the addressable memory by this process.
+                *
+                * Due to the typical kernel/user-space split (1GB/3GB, or
+                * 2GB/2GB), we have to conservatively assume the 32-bit
+                * processes can only address 2-3GB.  So refuse if the user
+                * tries to allocate more than 2GB.
+                */
+               ERROR("Total memory requested may exceed maximum "
+                               "addressable memory by this process\n");
+               BAD_ERROR("Requested memory size too large\n");
+       }
+}
+
+
+int get_default_phys_mem()
+{
+       /*
+        * get_physical_memory() relies on /proc being mounted.
+        * If it fails, issue a warning, and use
+        * SQUASHFS_LOWMEM / SQUASHFS_TAKE as default,
+        * and allow a larger value to be set with -mem.
+        */
+       int mem = get_physical_memory();
+
+       if(mem == 0) {
+               mem = SQUASHFS_LOWMEM / SQUASHFS_TAKE;
+
+               ERROR("Warning: Cannot get size of physical memory, probably "
+                               "because /proc is missing.\n");
+               ERROR("Warning: Defaulting to minimal use of %d Mbytes, use "
+                               "-mem to set a better value,\n", mem);
+               ERROR("Warning: or fix /proc.\n");
+       } else
+               mem /= SQUASHFS_TAKE;
+
+       if(sizeof(void *) == 4 && mem > 640) {
+               /*
+                * If we're running on a kernel with PAE or on a 64-bit kernel,
+                * the default memory usage can exceed the addressable
+                * memory by this process.
+                * Due to the typical kernel/user-space split (1GB/3GB, or
+                * 2GB/2GB), we have to conservatively assume the 32-bit
+                * processes can only address 2-3GB.  So limit the  default
+                * usage to 640M, which gives room for other data.
+                */
+               mem = 640;
+       }
+
+       return mem;
+}
+
+
+void calculate_queue_sizes(int mem, int *readq, int *fragq, int *bwriteq,
+                                                       int *fwriteq)
+{
+       *readq = mem / SQUASHFS_READQ_MEM;
+       *bwriteq = mem / SQUASHFS_BWRITEQ_MEM;
+       *fwriteq = mem / SQUASHFS_FWRITEQ_MEM;
+       *fragq = mem - *readq - *bwriteq - *fwriteq;
+}
+
+
+void open_log_file(char *filename)
+{
+       log_fd=fopen(filename, "w");
+       if(log_fd == NULL)
+               BAD_ERROR("Failed to open log file \"%s\" because %s\n", filename, strerror(errno));
+
+       logging=TRUE;
+}
+
+
+void check_env_var()
+{
+       char *time_string = getenv("SOURCE_DATE_EPOCH");
+       unsigned int time;
+
+       if(time_string != NULL) {
+               /*
+                * We cannot have both command-line options and environment
+                * variable trying to set the timestamp(s) at the same
+                * time.  Semantically both are FORCE options which cannot be
+                * over-ridden elsewhere (otherwise they can't be relied on).
+                *
+                * So refuse to continue if both are set.
+                */
+               if(mkfs_time_opt || all_time_opt)
+                       BAD_ERROR("SOURCE_DATE_EPOCH and command line options "
+                               "can't be used at the same time to set "
+                               "timestamp(s)\n");
+
+               if(!parse_num_unsigned(time_string, &time)) {
+                       ERROR("Env Var SOURCE_DATE_EPOCH has invalid time value\n");
+                       EXIT_MKSQUASHFS();
+               }
+
+               all_time = mkfs_time = time;
+               all_time_opt = mkfs_time_opt = TRUE;
+       }
+}
+
+
+#define VERSION() \
+       printf("mksquashfs version 4.4 (2019/08/29)\n");\
+       printf("copyright (C) 2019 Phillip Lougher "\
+               "<phillip@squashfs.org.uk>\n\n"); \
+       printf("This program is free software; you can redistribute it and/or"\
+               "\n");\
+       printf("modify it under the terms of the GNU General Public License"\
+               "\n");\
+       printf("as published by the Free Software Foundation; either version "\
+               "2,\n");\
+       printf("or (at your option) any later version.\n\n");\
+       printf("This program is distributed in the hope that it will be "\
+               "useful,\n");\
+       printf("but WITHOUT ANY WARRANTY; without even the implied warranty "\
+               "of\n");\
+       printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the"\
+               "\n");\
+       printf("GNU General Public License for more details.\n");
+int main(int argc, char *argv[])
+{
+       struct stat buf, source_buf;
+       int res, i;
+       char *b, *root_name = NULL;
+       int keep_as_directory = FALSE;
+       squashfs_inode inode;
+       int readq;
+       int fragq;
+       int bwriteq;
+       int fwriteq;
+       int total_mem = get_default_phys_mem();
+       int progress = TRUE;
+       int force_progress = FALSE;
+       struct file_buffer **fragment = NULL;
+
+       if(argc > 1 && strcmp(argv[1], "-version") == 0) {
+               VERSION();
+               exit(0);
+       }
+
+       block_log = slog(block_size);
+       calculate_queue_sizes(total_mem, &readq, &fragq, &bwriteq, &fwriteq);
+
+        for(i = 1; i < argc && argv[i][0] != '-'; i++);
+       if(i < 3)
+               goto printOptions;
+       source_path = argv + 1;
+       source = i - 2;
+
+       /*
+        * Scan the command line for -comp xxx option, this is to ensure
+        * any -X compressor specific options are passed to the
+        * correct compressor
+        */
+       for(; i < argc; i++) {
+               struct compressor *prev_comp = comp;
+               
+               if(strcmp(argv[i], "-comp") == 0) {
+                       if(++i == argc) {
+                               ERROR("%s: -comp missing compression type\n",
+                                       argv[0]);
+                               exit(1);
+                       }
+                       comp = lookup_compressor(argv[i]);
+                       if(!comp->supported) {
+                               ERROR("%s: Compressor \"%s\" is not supported!"
+                                       "\n", argv[0], argv[i]);
+                               ERROR("%s: Compressors available:\n", argv[0]);
+                               display_compressors("", COMP_DEFAULT);
+                               exit(1);
+                       }
+                       if(prev_comp != NULL && prev_comp != comp) {
+                               ERROR("%s: -comp multiple conflicting -comp"
+                                       " options specified on command line"
+                                       ", previously %s, now %s\n", argv[0],
+                                       prev_comp->name, comp->name);
+                               exit(1);
+                       }
+                       compressor_opt_parsed = 1;
+
+               } else if(strcmp(argv[i], "-e") == 0)
+                       break;
+               else if(strcmp(argv[i], "-root-becomes") == 0 ||
+                               strcmp(argv[i], "-ef") == 0 ||
+                               strcmp(argv[i], "-pf") == 0 ||
+                               strcmp(argv[i], "-vaf") == 0 ||
+                               strcmp(argv[i], "-log") == 0)
+                       i++;
+       }
+
+       /*
+        * if no -comp option specified lookup default compressor.  Note the
+        * Makefile ensures the default compressor has been built, and so we
+        * don't need to to check for failure here
+        */
+       if(comp == NULL)
+               comp = lookup_compressor(COMP_DEFAULT);
+
+       for(i = source + 2; i < argc; i++) {
+               if(strcmp(argv[i], "-mkfs-time") == 0 ||
+                               strcmp(argv[i], "-fstime") == 0) {
+                       if((++i == argc) || !parse_num_unsigned(argv[i], &mkfs_time)) {
+                               ERROR("%s: %s missing or invalid time value\n", argv[0], argv[i - 1]);
+                               exit(1);
+                       }
+                       mkfs_time_opt = TRUE;
+               } else if(strcmp(argv[i], "-all-time") == 0) {
+                       if((++i == argc) || !parse_num_unsigned(argv[i], &all_time)) {
+                               ERROR("%s: %s missing or invalid time value\n", argv[0], argv[i - 1]);
+                               exit(1);
+                       }
+                       all_time_opt = TRUE;
+                       clamping = FALSE;
+               } else if(strcmp(argv[i], "-reproducible") == 0)
+                       reproducible = TRUE;
+               else if(strcmp(argv[i], "-not-reproducible") == 0)
+                       reproducible = FALSE;
+               else if(strcmp(argv[i], "-root-mode") == 0) {
+                       if((++i == argc) || !parse_mode(argv[i], &root_mode)) {
+                               ERROR("%s: -root-mode missing or invalid mode,"
+                                       " octal number <= 07777 expected\n", argv[0]);
+                               exit(1);
+                       }
+                       root_mode_opt = TRUE;
+               } else if(strcmp(argv[i], "-log") == 0) {
+                       if(++i == argc) {
+                               ERROR("%s: %s missing log file\n",
+                                       argv[0], argv[i - 1]);
+                               exit(1);
+                       }
+                       open_log_file(argv[i]);
+
+               } else if(strcmp(argv[i], "-action") == 0 ||
+                               strcmp(argv[i], "-a") ==0) {
+                       if(++i == argc) {
+                               ERROR("%s: %s missing action\n",
+                                       argv[0], argv[i - 1]);
+                               exit(1);
+                       }
+                       res = parse_action(argv[i], ACTION_LOG_NONE);
+                       if(res == 0)
+                               exit(1);
+
+               } else if(strcmp(argv[i], "-verbose-action") == 0 ||
+                               strcmp(argv[i], "-va") ==0) {
+                       if(++i == argc) {
+                               ERROR("%s: %s missing action\n",
+                                       argv[0], argv[i - 1]);
+                               exit(1);
+                       }
+                       res = parse_action(argv[i], ACTION_LOG_VERBOSE);
+                       if(res == 0)
+                               exit(1);
+
+               } else if(strcmp(argv[i], "-true-action") == 0 ||
+                               strcmp(argv[i], "-ta") ==0) {
+                       if(++i == argc) {
+                               ERROR("%s: %s missing action\n",
+                                       argv[0], argv[i - 1]);
+                               exit(1);
+                       }
+                       res = parse_action(argv[i], ACTION_LOG_TRUE);
+                       if(res == 0)
+                               exit(1);
+
+               } else if(strcmp(argv[i], "-false-action") == 0 ||
+                               strcmp(argv[i], "-fa") ==0) {
+                       if(++i == argc) {
+                               ERROR("%s: %s missing action\n",
+                                       argv[0], argv[i - 1]);
+                               exit(1);
+                       }
+                       res = parse_action(argv[i], ACTION_LOG_FALSE);
+                       if(res == 0)
+                               exit(1);
+
+               } else if(strcmp(argv[i], "-action-file") == 0 ||
+                               strcmp(argv[i], "-af") ==0) {
+                       if(++i == argc) {
+                               ERROR("%s: %s missing filename\n", argv[0],
+                                                       argv[i - 1]);
+                               exit(1);
+                       }
+                       if(read_action_file(argv[i], ACTION_LOG_NONE) == FALSE)
+                               exit(1);
+
+               } else if(strcmp(argv[i], "-verbose-action-file") == 0 ||
+                               strcmp(argv[i], "-vaf") ==0) {
+                       if(++i == argc) {
+                               ERROR("%s: %s missing filename\n", argv[0],
+                                                       argv[i - 1]);
+                               exit(1);
+                       }
+                       if(read_action_file(argv[i], ACTION_LOG_VERBOSE) == FALSE)
+                               exit(1);
+
+               } else if(strcmp(argv[i], "-true-action-file") == 0 ||
+                               strcmp(argv[i], "-taf") ==0) {
+                       if(++i == argc) {
+                               ERROR("%s: %s missing filename\n", argv[0],
+                                                       argv[i - 1]);
+                               exit(1);
+                       }
+                       if(read_action_file(argv[i], ACTION_LOG_TRUE) == FALSE)
+                               exit(1);
+
+               } else if(strcmp(argv[i], "-false-action-file") == 0 ||
+                               strcmp(argv[i], "-faf") ==0) {
+                       if(++i == argc) {
+                               ERROR("%s: %s missing filename\n", argv[0],
+                                                       argv[i - 1]);
+                               exit(1);
+                       }
+                       if(read_action_file(argv[i], ACTION_LOG_FALSE) == FALSE)
+                               exit(1);
+
+               } else if(strcmp(argv[i], "-comp") == 0)
+                       /* parsed previously */
+                       i++;
+
+               else if(strncmp(argv[i], "-X", 2) == 0) {
+                       int args;
+
+                       if(strcmp(argv[i] + 2, "help") == 0)
+                               goto print_compressor_options;
+
+                       args = compressor_options(comp, argv + i, argc - i);
+                       if(args < 0) {
+                               if(args == -1) {
+                                       ERROR("%s: Unrecognised compressor"
+                                               " option %s\n", argv[0],
+                                               argv[i]);
+                                       if(!compressor_opt_parsed)
+                                               ERROR("%s: Did you forget to"
+                                                       " specify -comp?\n",
+                                                       argv[0]);
+print_compressor_options:
+                                       ERROR("%s: selected compressor \"%s\""
+                                               ".  Options supported: %s\n",
+                                               argv[0], comp->name,
+                                               comp->usage ? "" : "none");
+                                       if(comp->usage)
+                                               comp->usage();
+                               }
+                               exit(1);
+                       }
+                       i += args;
+
+               } else if(strcmp(argv[i], "-pf") == 0) {
+                       if(++i == argc) {
+                               ERROR("%s: -pf missing filename\n", argv[0]);
+                               exit(1);
+                       }
+                       if(read_pseudo_file(argv[i]) == FALSE)
+                               exit(1);
+               } else if(strcmp(argv[i], "-p") == 0) {
+                       if(++i == argc) {
+                               ERROR("%s: -p missing pseudo file definition\n",
+                                       argv[0]);
+                               exit(1);
+                       }
+                       if(read_pseudo_def(argv[i]) == FALSE)
+                               exit(1);
+               } else if(strcmp(argv[i], "-recover") == 0) {
+                       if(++i == argc) {
+                               ERROR("%s: -recover missing recovery file\n",
+                                       argv[0]);
+                               exit(1);
+                       }
+                       read_recovery_data(argv[i], argv[source + 1]);
+               } else if(strcmp(argv[i], "-no-recovery") == 0)
+                       recover = FALSE;
+               else if(strcmp(argv[i], "-wildcards") == 0) {
+                       old_exclude = FALSE;
+                       use_regex = FALSE;
+               } else if(strcmp(argv[i], "-regex") == 0) {
+                       old_exclude = FALSE;
+                       use_regex = TRUE;
+               } else if(strcmp(argv[i], "-no-sparse") == 0)
+                       sparse_files = FALSE;
+               else if(strcmp(argv[i], "-no-progress") == 0)
+                       progress = FALSE;
+               else if(strcmp(argv[i], "-progress") == 0)
+                       force_progress = TRUE;
+               else if(strcmp(argv[i], "-no-exports") == 0)
+                       exportable = FALSE;
+               else if(strcmp(argv[i], "-offset") == 0 || strcmp(argv[i], "-o") == 0) {
+                       if((++i == argc) || !parse_numberll(argv[i], &start_offset, 1)) {
+                               ERROR("%s: %s missing or invalid offset size\n", argv[0], argv[i - 1]);
+                               exit(1);
+                       }
+               } else if(strcmp(argv[i], "-processors") == 0) {
+                       if((++i == argc) || !parse_num(argv[i], &processors)) {
+                               ERROR("%s: -processors missing or invalid "
+                                       "processor number\n", argv[0]);
+                               exit(1);
+                       }
+                       if(processors < 1) {
+                               ERROR("%s: -processors should be 1 or larger\n",
+                                       argv[0]);
+                               exit(1);
+                       }
+               } else if(strcmp(argv[i], "-read-queue") == 0) {
+                       if((++i == argc) || !parse_num(argv[i], &readq)) {
+                               ERROR("%s: -read-queue missing or invalid "
+                                       "queue size\n", argv[0]);
+                               exit(1);
+                       }
+                       if(readq < 1) {
+                               ERROR("%s: -read-queue should be 1 megabyte or "
+                                       "larger\n", argv[0]);
+                               exit(1);
+                       }
+               } else if(strcmp(argv[i], "-write-queue") == 0) {
+                       if((++i == argc) || !parse_num(argv[i], &bwriteq)) {
+                               ERROR("%s: -write-queue missing or invalid "
+                                       "queue size\n", argv[0]);
+                               exit(1);
+                       }
+                       if(bwriteq < 2) {
+                               ERROR("%s: -write-queue should be 2 megabytes "
+                                       "or larger\n", argv[0]);
+                               exit(1);
+                       }
+                       fwriteq = bwriteq >> 1;
+                       bwriteq -= fwriteq;
+               } else if(strcmp(argv[i], "-fragment-queue") == 0) {
+                       if((++i == argc) || !parse_num(argv[i], &fragq)) {
+                               ERROR("%s: -fragment-queue missing or invalid "
+                                       "queue size\n", argv[0]);
+                               exit(1);
+                       }
+                       if(fragq < 1) {
+                               ERROR("%s: -fragment-queue should be 1 "
+                                       "megabyte or larger\n", argv[0]);
+                               exit(1);
+                       }
+               } else if(strcmp(argv[i], "-mem") == 0) {
+                       long long number;
+
+                       if((++i == argc) ||
+                                       !parse_numberll(argv[i], &number, 1)) {
+                               ERROR("%s: -mem missing or invalid mem size\n",
+                                        argv[0]);
+                               exit(1);
+                       }
+
+                       /*
+                        * convert from bytes to Mbytes, ensuring the value
+                        * does not overflow a signed int
+                        */
+                       if(number >= (1LL << 51)) {
+                               ERROR("%s: -mem invalid mem size\n", argv[0]);
+                               exit(1);
+                       }
+
+                       total_mem = number / 1048576;
+                       if(total_mem < (SQUASHFS_LOWMEM / SQUASHFS_TAKE)) {
+                               ERROR("%s: -mem should be %d Mbytes or "
+                                       "larger\n", argv[0],
+                                       SQUASHFS_LOWMEM / SQUASHFS_TAKE);
+                               exit(1);
+                       }
+                       calculate_queue_sizes(total_mem, &readq, &fragq,
+                               &bwriteq, &fwriteq);
+               } else if(strcmp(argv[i], "-b") == 0) {
+                       if(++i == argc) {
+                               ERROR("%s: -b missing block size\n", argv[0]);
+                               exit(1);
+                       }
+                       if(!parse_number(argv[i], &block_size, 1)) {
+                               ERROR("%s: -b invalid block size\n", argv[0]);
+                               exit(1);
+                       }
+                       if((block_log = slog(block_size)) == 0) {
+                               ERROR("%s: -b block size not power of two or "
+                                       "not between 4096 and 1Mbyte\n",
+                                       argv[0]);
+                               exit(1);
+                       }
+               } else if(strcmp(argv[i], "-ef") == 0) {
+                       if(++i == argc) {
+                               ERROR("%s: -ef missing filename\n", argv[0]);
+                               exit(1);
+                       }
+               } else if(strcmp(argv[i], "-no-duplicates") == 0)
+                       duplicate_checking = FALSE;
+
+               else if(strcmp(argv[i], "-no-fragments") == 0)
+                       no_fragments = TRUE;
+
+                else if(strcmp(argv[i], "-always-use-fragments") == 0)
+                       always_use_fragments = TRUE;
+
+                else if(strcmp(argv[i], "-sort") == 0) {
+                       if(++i == argc) {
+                               ERROR("%s: -sort missing filename\n", argv[0]);
+                               exit(1);
+                       }
+               } else if(strcmp(argv[i], "-all-root") == 0 ||
+                               strcmp(argv[i], "-root-owned") == 0)
+                       global_uid = global_gid = 0;
+
+               else if(strcmp(argv[i], "-force-uid") == 0) {
+                       if(++i == argc) {
+                               ERROR("%s: -force-uid missing uid or user\n",
+                                       argv[0]);
+                               exit(1);
+                       }
+                       if((global_uid = strtoll(argv[i], &b, 10)), *b =='\0') {
+                               if(global_uid < 0 || global_uid >
+                                               (((long long) 1 << 32) - 1)) {
+                                       ERROR("%s: -force-uid uid out of range"
+                                               "\n", argv[0]);
+                                       exit(1);
+                               }
+                       } else {
+                               struct passwd *uid = getpwnam(argv[i]);
+                               if(uid)
+                                       global_uid = uid->pw_uid;
+                               else {
+                                       ERROR("%s: -force-uid invalid uid or "
+                                               "unknown user\n", argv[0]);
+                                       exit(1);
+                               }
+                       }
+               } else if(strcmp(argv[i], "-force-gid") == 0) {
+                       if(++i == argc) {
+                               ERROR("%s: -force-gid missing gid or group\n",
+                                       argv[0]);
+                               exit(1);
+                       }
+                       if((global_gid = strtoll(argv[i], &b, 10)), *b =='\0') {
+                               if(global_gid < 0 || global_gid >
+                                               (((long long) 1 << 32) - 1)) {
+                                       ERROR("%s: -force-gid gid out of range"
+                                               "\n", argv[0]);
+                                       exit(1);
+                               }
+                       } else {
+                               struct group *gid = getgrnam(argv[i]);
+                               if(gid)
+                                       global_gid = gid->gr_gid;
+                               else {
+                                       ERROR("%s: -force-gid invalid gid or "
+                                               "unknown group\n", argv[0]);
+                                       exit(1);
+                               }
+                       }
+               } else if(strcmp(argv[i], "-noI") == 0 ||
+                               strcmp(argv[i], "-noInodeCompression") == 0)
+                       noI = TRUE;
+
+               else if(strcmp(argv[i], "-noId") == 0 ||
+                               strcmp(argv[i], "-noIdTableCompression") == 0)
+                       noId = TRUE;
+
+               else if(strcmp(argv[i], "-noD") == 0 ||
+                               strcmp(argv[i], "-noDataCompression") == 0)
+                       noD = TRUE;
+
+               else if(strcmp(argv[i], "-noF") == 0 ||
+                               strcmp(argv[i], "-noFragmentCompression") == 0)
+                       noF = TRUE;
+
+               else if(strcmp(argv[i], "-noX") == 0 ||
+                               strcmp(argv[i], "-noXattrCompression") == 0)
+                       noX = TRUE;
+
+               else if(strcmp(argv[i], "-no-xattrs") == 0)
+                       no_xattrs = TRUE;
+
+               else if(strcmp(argv[i], "-xattrs") == 0)
+                       no_xattrs = FALSE;
+
+               else if(strcmp(argv[i], "-nopad") == 0)
+                       nopad = TRUE;
+
+               else if(strcmp(argv[i], "-info") == 0)
+                       silent = FALSE;
+
+               else if(strcmp(argv[i], "-e") == 0)
+                       break;
+
+               else if(strcmp(argv[i], "-noappend") == 0)
+                       delete = TRUE;
+
+               else if(strcmp(argv[i], "-quiet") == 0)
+                       quiet = TRUE;
+
+               else if(strcmp(argv[i], "-keep-as-directory") == 0)
+                       keep_as_directory = TRUE;
+
+               else if(strcmp(argv[i], "-exit-on-error") == 0)
+                       exit_on_error = TRUE;
+
+               else if(strcmp(argv[i], "-root-becomes") == 0) {
+                       if(++i == argc) {
+                               ERROR("%s: -root-becomes: missing name\n",
+                                       argv[0]);
+                               exit(1);
+                       }       
+                       root_name = argv[i];
+               } else if(strcmp(argv[i], "-version") == 0) {
+                       VERSION();
+               } else {
+                       ERROR("%s: invalid option\n\n", argv[0]);
+printOptions:
+                       ERROR("SYNTAX:%s source1 source2 ...  dest [options] "
+                               "[-e list of exclude\ndirs/files]\n", argv[0]);
+                       ERROR("\nFilesystem build options:\n");
+                       ERROR("-comp <comp>\t\tselect <comp> compression\n");
+                       ERROR("\t\t\tCompressors available:\n");
+                       display_compressors("\t\t\t", COMP_DEFAULT);
+                       ERROR("-b <block_size>\t\tset data block to "
+                               "<block_size>.  Default 128 Kbytes\n");
+                       ERROR("\t\t\tOptionally a suffix of K or M can be"
+                               " given to specify\n\t\t\tKbytes or Mbytes"
+                               " respectively\n");
+                       ERROR("-reproducible\t\tbuild images that are reproducible"
+                               REP_STR "\n");
+                       ERROR("-not-reproducible\tbuild images that are not reproducible"
+                               NOREP_STR "\n");
+                       ERROR("-mkfs-time <time>\tset mkfs time to <time> which is an unsigned int\n");
+                       ERROR("-fstime <time>\t\tsynonym for mkfs-time\n");
+                       ERROR("-all-time <time>\tset all inode times to <time> which is an unsigned int\n");
+                       ERROR("-no-exports\t\tdon't make the filesystem "
+                               "exportable via NFS\n");
+                       ERROR("-no-sparse\t\tdon't detect sparse files\n");
+                       ERROR("-no-xattrs\t\tdon't store extended attributes"
+                               NOXOPT_STR "\n");
+                       ERROR("-xattrs\t\t\tstore extended attributes" XOPT_STR
+                               "\n");
+                       ERROR("-noI\t\t\tdo not compress inode table\n");
+                       ERROR("-noId\t\t\tdo not compress the uid/gid table"
+                               " (implied by -noI)\n");
+                       ERROR("-noD\t\t\tdo not compress data blocks\n");
+                       ERROR("-noF\t\t\tdo not compress fragment blocks\n");
+                       ERROR("-noX\t\t\tdo not compress extended "
+                               "attributes\n");
+                       ERROR("-no-fragments\t\tdo not use fragments\n");
+                       ERROR("-always-use-fragments\tuse fragment blocks for "
+                               "files larger than block size\n");
+                       ERROR("-no-duplicates\t\tdo not perform duplicate "
+                               "checking\n");
+                       ERROR("-all-root\t\tmake all files owned by root\n");
+                       ERROR("-root-mode <mode>\tset root directory permissions to octal <mode>\n");
+                       ERROR("-force-uid <uid>\tset all file uids to <uid>\n");
+                       ERROR("-force-gid <gid>\tset all file gids to <gid>\n");
+                       ERROR("-nopad\t\t\tdo not pad filesystem to a multiple "
+                               "of 4K\n");
+                       ERROR("-keep-as-directory\tif one source directory is "
+                               "specified, create a root\n");
+                       ERROR("\t\t\tdirectory containing that directory, "
+                               "rather than the\n");
+                       ERROR("\t\t\tcontents of the directory\n");
+                       ERROR("\nFilesystem filter options:\n");
+                       ERROR("-p <pseudo-definition>\tAdd pseudo file "
+                               "definition\n");
+                       ERROR("-pf <pseudo-file>\tAdd list of pseudo file "
+                               "definitions\n");
+                       ERROR("\t\t\tPseudo definitions should be of the "
+                               "format\n");
+                       ERROR("\t\t\t\tfilename d mode uid gid\n");
+                       ERROR("\t\t\t\tfilename m mode uid gid\n");
+                       ERROR("\t\t\t\tfilename b mode uid gid major minor\n");
+                       ERROR("\t\t\t\tfilename c mode uid gid major minor\n");
+                       ERROR("\t\t\t\tfilename f mode uid gid command\n");
+                       ERROR("\t\t\t\tfilename s mode uid gid symlink\n");
+                       ERROR("-sort <sort_file>\tsort files according to "
+                               "priorities in <sort_file>.  One\n");
+                       ERROR("\t\t\tfile or dir with priority per line.  "
+                               "Priority -32768 to\n");
+                       ERROR("\t\t\t32767, default priority 0\n");
+                       ERROR("-ef <exclude_file>\tlist of exclude dirs/files."
+                               "  One per line\n");
+                       ERROR("-wildcards\t\tAllow extended shell wildcards "
+                               "(globbing) to be used in\n\t\t\texclude "
+                               "dirs/files\n");
+                       ERROR("-regex\t\t\tAllow POSIX regular expressions to "
+                               "be used in exclude\n\t\t\tdirs/files\n");
+                       ERROR("\nFilesystem append options:\n");
+                       ERROR("-noappend\t\tdo not append to existing "
+                               "filesystem\n");
+                       ERROR("-root-becomes <name>\twhen appending source "
+                               "files/directories, make the\n");
+                       ERROR("\t\t\toriginal root become a subdirectory in "
+                               "the new root\n");
+                       ERROR("\t\t\tcalled <name>, rather than adding the new "
+                               "source items\n");
+                       ERROR("\t\t\tto the original root\n");
+                       ERROR("\nMksquashfs runtime options:\n");
+                       ERROR("-version\t\tprint version, licence and "
+                               "copyright message\n");
+                       ERROR("-exit-on-error\t\ttreat normally ignored errors "
+                               "as fatal\n");
+                       ERROR("-recover <name>\t\trecover filesystem data "
+                               "using recovery file <name>\n");
+                       ERROR("-no-recovery\t\tdon't generate a recovery "
+                               "file\n");
+                       ERROR("-quiet\t\t\tno verbose output\n");
+                       ERROR("-info\t\t\tprint files written to filesystem\n");
+                       ERROR("-no-progress\t\tdon't display the progress "
+                               "bar\n");
+                       ERROR("-progress\t\tdisplay progress bar when using "
+                               "the -info option\n");
+                       ERROR("-processors <number>\tUse <number> processors."
+                               "  By default will use number of\n");
+                       ERROR("\t\t\tprocessors available\n");
+                       ERROR("-mem <size>\t\tUse <size> physical memory.  "
+                               "Currently set to %dM\n", total_mem);
+                       ERROR("\t\t\tOptionally a suffix of K, M or G can be"
+                               " given to specify\n\t\t\tKbytes, Mbytes or"
+                               " Gbytes respectively\n");
+                       ERROR("\nMiscellaneous options:\n");
+                       ERROR("-root-owned\t\talternative name for -all-root"
+                               "\n");
+                       ERROR("-offset <offset>\tSkip <offset> bytes at the "
+                               "beginning of <dest>.\n");
+                       ERROR("\t\t\tOptionally a suffix of K, M or G can be"
+                               " given to specify\n\t\t\tKbytes, Mbytes or"
+                               " Gbytes respectively.\n");
+                       ERROR("\t\t\tDefault 0 bytes.\n");
+                       ERROR("-o <offset>\t\tsynonym for -offset\n");
+                       ERROR("-noInodeCompression\talternative name for -noI"
+                               "\n");
+                       ERROR("-noIdTableCompression\talternative name for -noId"
+                               "\n");
+                       ERROR("-noDataCompression\talternative name for -noD"
+                               "\n");
+                       ERROR("-noFragmentCompression\talternative name for "
+                               "-noF\n");
+                       ERROR("-noXattrCompression\talternative name for "
+                               "-noX\n");
+                       ERROR("\n-Xhelp\t\t\tprint compressor options for"
+                               " selected compressor\n");
+                       ERROR("\nCompressors available and compressor specific "
+                               "options:\n");
+                       display_compressor_usage(COMP_DEFAULT);
+                       exit(1);
+               }
+       }
+
+       check_env_var();
+
+       /*
+        * The -noI option implies -noId for backwards compatibility, so reset noId
+        * if both have been specified
+        */
+       if(noI && noId)
+               noId = FALSE;
+
+       /*
+        * Some compressors may need the options to be checked for validity
+        * once all the options have been processed
+        */
+       res = compressor_options_post(comp, block_size);
+       if(res)
+               EXIT_MKSQUASHFS();
+
+       /*
+        * If the -info option has been selected then disable the
+        * progress bar unless it has been explicitly enabled with
+        * the -progress option
+        */
+       if(!silent)
+               progress = force_progress;
+               
+#ifdef SQUASHFS_TRACE
+       /*
+        * Disable progress bar if full debug tracing is enabled.
+        * The progress bar in this case just gets in the way of the
+        * debug trace output
+        */
+       progress = FALSE;
+#endif
+
+       for(i = 0; i < source; i++)
+               if(lstat(source_path[i], &source_buf) == -1) {
+                       fprintf(stderr, "Cannot stat source directory \"%s\" "
+                               "because %s\n", source_path[i],
+                               strerror(errno));
+                       EXIT_MKSQUASHFS();
+               }
+
+       destination_file = argv[source + 1];
+       if(stat(argv[source + 1], &buf) == -1) {
+               if(errno == ENOENT) { /* Does not exist */
+                       fd = open(argv[source + 1], O_CREAT | O_TRUNC | O_RDWR,
+                               S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
+                       if(fd == -1) {
+                               perror("Could not create destination file");
+                               exit(1);
+                       }
+                       delete = TRUE;
+               } else {
+                       perror("Could not stat destination file");
+                       exit(1);
+               }
+
+       } else {
+               if(S_ISBLK(buf.st_mode)) {
+                       if((fd = open(argv[source + 1], O_RDWR)) == -1) {
+                               perror("Could not open block device as "
+                                       "destination");
+                               exit(1);
+                       }
+                       block_device = 1;
+
+               } else if(S_ISREG(buf.st_mode))  {
+                       fd = open(argv[source + 1], (delete ? O_TRUNC : 0) |
+                               O_RDWR);
+                       if(fd == -1) {
+                               perror("Could not open regular file for "
+                                       "writing as destination");
+                               exit(1);
+                       }
+               }
+               else {
+                       ERROR("Destination not block device or regular file\n");
+                       exit(1);
+               }
+
+       }
+
+       /*
+        * process the exclude files - must be done afer destination file has
+        * been possibly created
+        */
+       for(i = source + 2; i < argc; i++)
+               if(strcmp(argv[i], "-ef") == 0)
+                       /*
+                        * Note presence of filename arg has already
+                        * been checked
+                        */
+                       process_exclude_file(argv[++i]);
+               else if(strcmp(argv[i], "-e") == 0)
+                       break;
+               else if(strcmp(argv[i], "-root-becomes") == 0 ||
+                               strcmp(argv[i], "-sort") == 0 ||
+                               strcmp(argv[i], "-pf") == 0 ||
+                               strcmp(argv[i], "-af") == 0 ||
+                               strcmp(argv[i], "-vaf") == 0 ||
+                               strcmp(argv[i], "-comp") == 0 ||
+                               strcmp(argv[i], "-log") == 0)
+                       i++;
+
+       if(i != argc) {
+               if(++i == argc) {
+                       ERROR("%s: -e missing arguments\n", argv[0]);
+                       EXIT_MKSQUASHFS();
+               }
+               while(i < argc)
+                       if(old_exclude)
+                               old_add_exclude(argv[i++]);
+                       else
+                               add_exclude(argv[i++]);
+       }
+
+       /* process the sort files - must be done afer the exclude files  */
+       for(i = source + 2; i < argc; i++)
+               if(strcmp(argv[i], "-sort") == 0) {
+                       int res = read_sort_file(argv[++i], source,
+                                                               source_path);
+                       if(res == FALSE)
+                               BAD_ERROR("Failed to read sort file\n");
+                       sorted ++;
+               } else if(strcmp(argv[i], "-e") == 0)
+                       break;
+               else if(strcmp(argv[i], "-root-becomes") == 0 ||
+                               strcmp(argv[i], "-ef") == 0 ||
+                               strcmp(argv[i], "-pf") == 0 ||
+                               strcmp(argv[i], "-af") == 0 ||
+                               strcmp(argv[i], "-vaf") == 0 ||
+                               strcmp(argv[i], "-comp") == 0 ||
+                               strcmp(argv[i], "-log") == 0)
+                       i++;
+
+       if(!delete) {
+               comp = read_super(fd, &sBlk, argv[source + 1]);
+               if(comp == NULL) {
+                       ERROR("Failed to read existing filesystem - will not "
+                               "overwrite - ABORTING!\n");
+                       ERROR("To force Mksquashfs to write to this block "
+                               "device or file use -noappend\n");
+                       EXIT_MKSQUASHFS();
+               }
+
+               block_log = slog(block_size = sBlk.block_size);
+               noI = SQUASHFS_UNCOMPRESSED_INODES(sBlk.flags);
+               noD = SQUASHFS_UNCOMPRESSED_DATA(sBlk.flags);
+               noF = SQUASHFS_UNCOMPRESSED_FRAGMENTS(sBlk.flags);
+               noX = SQUASHFS_UNCOMPRESSED_XATTRS(sBlk.flags);
+               noId = SQUASHFS_UNCOMPRESSED_IDS(sBlk.flags);
+               no_fragments = SQUASHFS_NO_FRAGMENTS(sBlk.flags);
+               always_use_fragments = SQUASHFS_ALWAYS_FRAGMENTS(sBlk.flags);
+               duplicate_checking = SQUASHFS_DUPLICATES(sBlk.flags);
+               exportable = SQUASHFS_EXPORTABLE(sBlk.flags);
+               no_xattrs = SQUASHFS_NO_XATTRS(sBlk.flags);
+               comp_opts = SQUASHFS_COMP_OPTS(sBlk.flags);
+       }
+
+       initialise_threads(readq, fragq, bwriteq, fwriteq, delete,
+               destination_file);
+
+       res = compressor_init(comp, &stream, SQUASHFS_METADATA_SIZE, 0);
+       if(res)
+               BAD_ERROR("compressor_init failed\n");
+
+       if(delete) {
+               int size;
+               void *comp_data = compressor_dump_options(comp, block_size,
+                       &size);
+
+               if(!quiet)
+                       printf("Creating %d.%d filesystem on %s, block size %d.\n",
+                               SQUASHFS_MAJOR, SQUASHFS_MINOR,
+                               argv[source + 1], block_size);
+
+               /*
+                * store any compressor specific options after the superblock,
+                * and set the COMP_OPT flag to show that the filesystem has
+                * compressor specfic options
+                */
+               if(comp_data) {
+                       unsigned short c_byte = size | SQUASHFS_COMPRESSED_BIT;
+       
+                       SQUASHFS_INSWAP_SHORTS(&c_byte, 1);
+                       write_destination(fd, sizeof(struct squashfs_super_block),
+                               sizeof(c_byte), &c_byte);
+                       write_destination(fd, sizeof(struct squashfs_super_block) +
+                               sizeof(c_byte), size, comp_data);
+                       bytes = sizeof(struct squashfs_super_block) + sizeof(c_byte)
+                               + size;
+                       comp_opts = TRUE;
+               } else                  
+                       bytes = sizeof(struct squashfs_super_block);
+       } else {
+               unsigned int last_directory_block, inode_dir_offset,
+                       inode_dir_file_size, root_inode_size,
+                       inode_dir_start_block, uncompressed_data,
+                       compressed_data, inode_dir_inode_number,
+                       inode_dir_parent_inode;
+               unsigned int root_inode_start =
+                       SQUASHFS_INODE_BLK(sBlk.root_inode),
+                       root_inode_offset =
+                       SQUASHFS_INODE_OFFSET(sBlk.root_inode);
+
+               if((bytes = read_filesystem(root_name, fd, &sBlk, &inode_table,
+                               &data_cache, &directory_table,
+                               &directory_data_cache, &last_directory_block,
+                               &inode_dir_offset, &inode_dir_file_size,
+                               &root_inode_size, &inode_dir_start_block,
+                               &file_count, &sym_count, &dev_count, &dir_count,
+                               &fifo_count, &sock_count, &total_bytes,
+                               &total_inode_bytes, &total_directory_bytes,
+                               &inode_dir_inode_number,
+                               &inode_dir_parent_inode, add_old_root_entry,
+                               &fragment_table, &inode_lookup_table)) == 0) {
+                       ERROR("Failed to read existing filesystem - will not "
+                               "overwrite - ABORTING!\n");
+                       ERROR("To force Mksquashfs to write to this block "
+                               "device or file use -noappend\n");
+                       EXIT_MKSQUASHFS();
+               }
+               if((append_fragments = fragments = sBlk.fragments)) {
+                       fragment_table = realloc((char *) fragment_table,
+                               ((fragments + FRAG_SIZE - 1) & ~(FRAG_SIZE - 1))
+                                * sizeof(struct squashfs_fragment_entry)); 
+                       if(fragment_table == NULL)
+                               BAD_ERROR("Out of memory in save filesystem state\n");
+               }
+
+               printf("Appending to existing %d.%d filesystem on %s, block "
+                       "size %d\n", SQUASHFS_MAJOR, SQUASHFS_MINOR, argv[source + 1],
+                       block_size);
+               printf("All -b, -noI, -noD, -noF, -noX, -noId, -no-duplicates, "
+                       "-no-fragments,\n-always-use-fragments, -exportable and "
+                       "-comp options ignored\n");
+               printf("\nIf appending is not wanted, please re-run with "
+                       "-noappend specified!\n\n");
+
+               compressed_data = (inode_dir_offset + inode_dir_file_size) &
+                       ~(SQUASHFS_METADATA_SIZE - 1);
+               uncompressed_data = (inode_dir_offset + inode_dir_file_size) &
+                       (SQUASHFS_METADATA_SIZE - 1);
+               
+               /* save original filesystem state for restoring ... */
+               sfragments = fragments;
+               sbytes = bytes;
+               sinode_count = sBlk.inodes;
+               scache_bytes = root_inode_offset + root_inode_size;
+               sdirectory_cache_bytes = uncompressed_data;
+               sdata_cache = malloc(scache_bytes);
+               if(sdata_cache == NULL)
+                       BAD_ERROR("Out of memory in save filesystem state\n");
+               sdirectory_data_cache = malloc(sdirectory_cache_bytes);
+               if(sdirectory_data_cache == NULL)
+                       BAD_ERROR("Out of memory in save filesystem state\n");
+               memcpy(sdata_cache, data_cache, scache_bytes);
+               memcpy(sdirectory_data_cache, directory_data_cache +
+                       compressed_data, sdirectory_cache_bytes);
+               sinode_bytes = root_inode_start;
+               stotal_bytes = total_bytes;
+               stotal_inode_bytes = total_inode_bytes;
+               stotal_directory_bytes = total_directory_bytes +
+                       compressed_data;
+               sfile_count = file_count;
+               ssym_count = sym_count;
+               sdev_count = dev_count;
+               sdir_count = dir_count + 1;
+               sfifo_count = fifo_count;
+               ssock_count = sock_count;
+               sdup_files = dup_files;
+               sid_count = id_count;
+               write_recovery_data(&sBlk);
+               save_xattrs();
+               appending = TRUE;
+
+               /*
+                * set the filesystem state up to be able to append to the
+                * original filesystem.  The filesystem state differs depending
+                * on whether we're appending to the original root directory, or
+                * if the original root directory becomes a sub-directory
+                * (root-becomes specified on command line, here root_name !=
+                * NULL)
+                */
+               inode_bytes = inode_size = root_inode_start;
+               directory_size = last_directory_block;
+               cache_size = root_inode_offset + root_inode_size;
+               directory_cache_size = inode_dir_offset + inode_dir_file_size;
+               if(root_name) {
+                       sdirectory_bytes = last_directory_block;
+                       sdirectory_compressed_bytes = 0;
+                       root_inode_number = inode_dir_parent_inode;
+                       inode_no = sBlk.inodes + 2;
+                       directory_bytes = last_directory_block;
+                       directory_cache_bytes = uncompressed_data;
+                       memmove(directory_data_cache, directory_data_cache +
+                               compressed_data, uncompressed_data);
+                       cache_bytes = root_inode_offset + root_inode_size;
+                       add_old_root_entry(root_name, sBlk.root_inode,
+                               inode_dir_inode_number, SQUASHFS_DIR_TYPE);
+                       total_directory_bytes += compressed_data;
+                       dir_count ++;
+               } else {
+                       sdirectory_compressed_bytes = last_directory_block -
+                               inode_dir_start_block;
+                       sdirectory_compressed =
+                               malloc(sdirectory_compressed_bytes);
+                       if(sdirectory_compressed == NULL)
+                               BAD_ERROR("Out of memory in save filesystem "
+                                       "state\n");
+                       memcpy(sdirectory_compressed, directory_table +
+                               inode_dir_start_block,
+                               sdirectory_compressed_bytes); 
+                       sdirectory_bytes = inode_dir_start_block;
+                       root_inode_number = inode_dir_inode_number;
+                       inode_no = sBlk.inodes + 1;
+                       directory_bytes = inode_dir_start_block;
+                       directory_cache_bytes = inode_dir_offset;
+                       cache_bytes = root_inode_offset;
+               }
+
+               inode_count = file_count + dir_count + sym_count + dev_count +
+                       fifo_count + sock_count;
+       }
+
+       if(path)
+               paths = add_subdir(paths, path);
+
+       dump_actions(); 
+       dump_pseudos();
+
+       if(delete && !keep_as_directory && source == 1 &&
+                       S_ISDIR(source_buf.st_mode))
+               dir_scan(&inode, source_path[0], scan1_readdir, progress);
+       else if(!keep_as_directory && source == 1 &&
+                       S_ISDIR(source_buf.st_mode))
+               dir_scan(&inode, source_path[0], scan1_single_readdir, progress);
+       else
+               dir_scan(&inode, "", scan1_encomp_readdir, progress);
+       sBlk.root_inode = inode;
+       sBlk.inodes = inode_count;
+       sBlk.s_magic = SQUASHFS_MAGIC;
+       sBlk.s_major = SQUASHFS_MAJOR;
+       sBlk.s_minor = SQUASHFS_MINOR;
+       sBlk.block_size = block_size;
+       sBlk.block_log = block_log;
+       sBlk.flags = SQUASHFS_MKFLAGS(noI, noD, noF, noX, noId, no_fragments,
+               always_use_fragments, duplicate_checking, exportable,
+               no_xattrs, comp_opts);
+       sBlk.mkfs_time = mkfs_time_opt ? mkfs_time : time(NULL);
+
+       disable_info();
+
+       while((fragment = get_frag_action(fragment)))
+               write_fragment(*fragment);
+       if(!reproducible)
+               unlock_fragments();
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex);
+       pthread_mutex_lock(&fragment_mutex);
+       while(fragments_outstanding) {
+               pthread_mutex_unlock(&fragment_mutex);
+               pthread_testcancel();
+               sched_yield();
+               pthread_mutex_lock(&fragment_mutex);
+       }
+       pthread_cleanup_pop(1);
+
+       queue_put(to_writer, NULL);
+       if(queue_get(from_writer) != 0)
+               EXIT_MKSQUASHFS();
+
+       set_progressbar_state(FALSE);
+       write_filesystem_tables(&sBlk, nopad);
+
+       if(logging)
+               fclose(log_fd);
+
+       return 0;
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/mksquashfs.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/mksquashfs.h
new file mode 100644 (file)
index 0000000..1beefef
--- /dev/null
@@ -0,0 +1,165 @@
+#ifndef MKSQUASHFS_H
+#define MKSQUASHFS_H
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
+ * 2012, 2013, 2014, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * mksquashfs.h
+ *
+ */
+
+struct dir_info {
+       char                    *pathname;
+       char                    *subpath;
+       unsigned int            count;
+       unsigned int            directory_count;
+       int                     depth;
+       unsigned int            excluded;
+       char                    dir_is_ldir;
+       struct dir_ent          *dir_ent;
+       struct dir_ent          *list;
+       DIR                     *linuxdir;
+};
+
+struct dir_ent {
+       char                    *name;
+       char                    *source_name;
+       char                    *nonstandard_pathname;
+       struct inode_info       *inode;
+       struct dir_info         *dir;
+       struct dir_info         *our_dir;
+       struct dir_ent          *next;
+};
+
+struct inode_info {
+       struct stat             buf;
+       struct inode_info       *next;
+       squashfs_inode          inode;
+       unsigned int            inode_number;
+       unsigned int            nlink;
+       int                     pseudo_id;
+       char                    type;
+       char                    read;
+       char                    root_entry;
+       char                    pseudo_file;
+       char                    no_fragments;
+       char                    always_use_fragments;
+       char                    noD;
+       char                    noF;
+       char                    symlink[0];
+};
+
+/* in memory file info */
+struct file_info {
+       long long               file_size;
+       long long               bytes;
+       long long               start;
+       unsigned int            *block_list;
+       struct file_info        *next;
+       struct fragment         *fragment;
+       unsigned short          checksum;
+       unsigned short          fragment_checksum;
+       char                    have_frag_checksum;
+       char                    have_checksum;
+};
+
+/* fragment block data structures */
+struct fragment {
+       unsigned int            index;
+       int                     offset;
+       int                     size;
+};
+
+/* in memory uid tables */
+#define ID_ENTRIES 256
+#define ID_HASH(id) (id & (ID_ENTRIES - 1))
+#define ISA_UID 1
+#define ISA_GID 2
+
+struct id {
+       unsigned int id;
+       int     index;
+       char    flags;
+       struct id *next;
+};
+
+/* fragment to file mapping used when appending */
+struct append_file {
+       struct file_info *file;
+       struct append_file *next;
+};
+
+#define PSEUDO_FILE_OTHER      1
+#define PSEUDO_FILE_PROCESS    2
+
+#define IS_PSEUDO(a)           ((a)->pseudo_file)
+#define IS_PSEUDO_PROCESS(a)   ((a)->pseudo_file & PSEUDO_FILE_PROCESS)
+#define IS_PSEUDO_OTHER(a)     ((a)->pseudo_file & PSEUDO_FILE_OTHER)
+
+/*
+ * Amount of physical memory to use by default, and the default queue
+ * ratios
+ */
+#define SQUASHFS_TAKE 4
+#define SQUASHFS_READQ_MEM 4
+#define SQUASHFS_BWRITEQ_MEM 4
+#define SQUASHFS_FWRITEQ_MEM 4
+
+/*
+ * Lowest amount of physical memory considered viable for Mksquashfs
+ * to run in Mbytes
+ */
+#define SQUASHFS_LOWMEM 64
+
+/* offset of data in compressed metadata blocks (allowing room for
+ * compressed size */
+#define BLOCK_OFFSET 2
+
+#ifdef REPRODUCIBLE_DEFAULT
+#define NOREP_STR
+#define REP_STR " (default)"
+#define REP_DEF 1
+#else
+#define NOREP_STR " (default)"
+#define REP_STR
+#define REP_DEF 0
+#endif
+
+extern struct cache *reader_buffer, *fragment_buffer, *reserve_cache;
+struct cache *bwriter_buffer, *fwriter_buffer;
+extern struct queue *to_reader, *to_deflate, *to_writer, *from_writer,
+       *to_frag, *locked_fragment, *to_process_frag;
+extern struct append_file **file_mapping;
+extern struct seq_queue *to_main, *to_order;
+extern pthread_mutex_t fragment_mutex, dup_mutex;
+extern struct squashfs_fragment_entry *fragment_table;
+extern struct compressor *comp;
+extern int block_size;
+extern struct file_info *dupl[];
+extern int read_fs_bytes(int, long long, int, void *);
+extern void add_file(long long, long long, long long, unsigned int *, int,
+       unsigned int, int, int);
+extern struct id *create_id(unsigned int);
+extern unsigned int get_uid(unsigned int);
+extern unsigned int get_guid(unsigned int);
+extern int read_bytes(int, void *, int);
+extern unsigned short get_checksum_mem(char *, int);
+extern int reproducible;
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/process_fragments.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/process_fragments.c
new file mode 100644 (file)
index 0000000..e3dc7c9
--- /dev/null
@@ -0,0 +1,371 @@
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2014
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * process_fragments.c
+ */
+
+#include <pthread.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <string.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "caches-queues-lists.h"
+#include "squashfs_fs.h"
+#include "mksquashfs.h"
+#include "error.h"
+#include "progressbar.h"
+#include "info.h"
+#include "compressor.h"
+#include "process_fragments.h"
+
+#define FALSE 0
+#define TRUE 1
+
+extern struct queue *to_process_frag;
+extern struct seq_queue *to_main;
+extern int sparse_files;
+extern long long start_offset;
+
+/*
+ * Compute 16 bit BSD checksum over the data, and check for sparseness
+ */
+static int checksum_sparse(struct file_buffer *file_buffer)
+{
+       unsigned char *b = (unsigned char *) file_buffer->data;
+       unsigned short chksum = 0;
+       int bytes = file_buffer->size, sparse = TRUE, value;
+
+       while(bytes --) {
+               chksum = (chksum & 1) ? (chksum >> 1) | 0x8000 : chksum >> 1;
+               value = *b++;
+               if(value) {
+                       sparse = FALSE;
+                       chksum += value;
+               }
+       }
+
+       file_buffer->checksum = chksum;
+       return sparse;
+}
+
+
+static int read_filesystem(int fd, long long byte, int bytes, void *buff)
+{
+       off_t off = byte;
+
+       TRACE("read_filesystem: reading from position 0x%llx, bytes %d\n",
+               byte, bytes);
+
+       if(lseek(fd, start_offset + off, SEEK_SET) == -1) {
+               ERROR("read_filesystem: Lseek on destination failed because %s, "
+                       "offset=0x%llx\n", strerror(errno), start_offset + off);
+               return 0;
+       } else if(read_bytes(fd, buff, bytes) < bytes) {
+               ERROR("Read on destination failed\n");
+               return 0;
+       }
+
+       return 1;
+}
+
+
+static struct file_buffer *get_fragment(struct fragment *fragment,
+       char *data_buffer, int fd)
+{
+       struct squashfs_fragment_entry *disk_fragment;
+       struct file_buffer *buffer, *compressed_buffer;
+       long long start_block;
+       int res, size, index = fragment->index;
+       char locked;
+
+       /*
+        * Lookup fragment block in cache.
+        * If the fragment block doesn't exist, then get the compressed version
+        * from the writer cache or off disk, and decompress it.
+        *
+        * This routine has two things which complicate the code:
+        *
+        *      1. Multiple threads can simultaneously lookup/create the
+        *         same buffer.  This means a buffer needs to be "locked"
+        *         when it is being filled in, to prevent other threads from
+        *         using it when it is not ready.  This is because we now do
+        *         fragment duplicate checking in parallel.
+        *      2. We have two caches which need to be checked for the
+        *         presence of fragment blocks: the normal fragment cache
+        *         and a "reserve" cache.  The reserve cache is used to
+        *         prevent an unnecessary pipeline stall when the fragment cache
+        *         is full of fragments waiting to be compressed.
+        */
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex);
+       pthread_mutex_lock(&dup_mutex);
+
+again:
+       buffer = cache_lookup_nowait(fragment_buffer, index, &locked);
+       if(buffer) {
+               pthread_mutex_unlock(&dup_mutex);
+               if(locked)
+                       /* got a buffer being filled in.  Wait for it */
+                       cache_wait_unlock(buffer);
+               goto finished;
+       }
+
+       /* not in fragment cache, is it in the reserve cache? */
+       buffer = cache_lookup_nowait(reserve_cache, index, &locked);
+       if(buffer) {
+               pthread_mutex_unlock(&dup_mutex);
+               if(locked)
+                       /* got a buffer being filled in.  Wait for it */
+                       cache_wait_unlock(buffer);
+               goto finished;
+       }
+
+       /* in neither cache, try to get it from the fragment cache */
+       buffer = cache_get_nowait(fragment_buffer, index);
+       if(!buffer) {
+               /*
+                * no room, get it from the reserve cache, this is
+                * dimensioned so it will always have space (no more than
+                * processors + 1 can have an outstanding reserve buffer)
+                */
+               buffer = cache_get_nowait(reserve_cache, index);
+               if(!buffer) {
+                       /* failsafe */
+                       ERROR("no space in reserve cache\n");
+                       goto again;
+               }
+       }
+
+       pthread_mutex_unlock(&dup_mutex);
+
+       compressed_buffer = cache_lookup(fwriter_buffer, index);
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &fragment_mutex);
+       pthread_mutex_lock(&fragment_mutex);
+       disk_fragment = &fragment_table[index];
+       size = SQUASHFS_COMPRESSED_SIZE_BLOCK(disk_fragment->size);
+       start_block = disk_fragment->start_block;
+       pthread_cleanup_pop(1);
+
+       if(SQUASHFS_COMPRESSED_BLOCK(disk_fragment->size)) {
+               int error;
+               char *data;
+
+               if(compressed_buffer)
+                       data = compressed_buffer->data;
+               else {
+                       res = read_filesystem(fd, start_block, size, data_buffer);
+                       if(res == 0) {
+                               ERROR("Failed to read fragment from output"
+                                       " filesystem\n");
+                               BAD_ERROR("Output filesystem corrupted?\n");
+                       }
+                       data = data_buffer;
+               }
+
+               res = compressor_uncompress(comp, buffer->data, data, size,
+                       block_size, &error);
+               if(res == -1)
+                       BAD_ERROR("%s uncompress failed with error code %d\n",
+                               comp->name, error);
+       } else if(compressed_buffer)
+               memcpy(buffer->data, compressed_buffer->data, size);
+       else {
+               res = read_filesystem(fd, start_block, size, buffer->data);
+               if(res == 0) {
+                       ERROR("Failed to read fragment from output "
+                               "filesystem\n");
+                       BAD_ERROR("Output filesystem corrupted?\n");
+               }
+       }
+
+       cache_unlock(buffer);
+       cache_block_put(compressed_buffer);
+
+finished:
+       pthread_cleanup_pop(0);
+
+       return buffer;
+}
+
+
+struct file_buffer *get_fragment_cksum(struct file_info *file,
+       char *data_buffer, int fd, unsigned short *checksum)
+{
+       struct file_buffer *frag_buffer;
+       struct append_file *append;
+       int index = file->fragment->index;
+
+       frag_buffer = get_fragment(file->fragment, data_buffer, fd);
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex);
+
+       for(append = file_mapping[index]; append; append = append->next) {
+               int offset = append->file->fragment->offset;
+               int size = append->file->fragment->size;
+               char *data = frag_buffer->data + offset;
+               unsigned short cksum = get_checksum_mem(data, size);
+
+               if(file == append->file)
+                       *checksum = cksum;
+
+               pthread_mutex_lock(&dup_mutex);
+               append->file->fragment_checksum = cksum;
+               append->file->have_frag_checksum = TRUE;
+               pthread_mutex_unlock(&dup_mutex);
+       }
+
+       pthread_cleanup_pop(0);
+
+       return frag_buffer;
+}
+
+
+void *frag_thrd(void *destination_file)
+{
+       sigset_t sigmask, old_mask;
+       char *data_buffer;
+       int fd;
+
+       sigemptyset(&sigmask);
+       sigaddset(&sigmask, SIGINT);
+       sigaddset(&sigmask, SIGTERM);
+       sigaddset(&sigmask, SIGUSR1);
+       pthread_sigmask(SIG_BLOCK, &sigmask, &old_mask);
+
+       fd = open(destination_file, O_RDONLY);
+       if(fd == -1)
+               BAD_ERROR("frag_thrd: can't open destination for reading\n");
+
+       data_buffer = malloc(SQUASHFS_FILE_MAX_SIZE);
+       if(data_buffer == NULL)
+               MEM_ERROR();
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &dup_mutex);
+
+       while(1) {
+               struct file_buffer *file_buffer = queue_get(to_process_frag);
+               struct file_buffer *buffer;
+               int sparse = checksum_sparse(file_buffer);
+               struct file_info *dupl_ptr;
+               long long file_size;
+               unsigned short checksum;
+               char flag;
+               int res;
+
+               if(sparse_files && sparse) {
+                       file_buffer->c_byte = 0;
+                       file_buffer->fragment = FALSE;
+               } else
+                       file_buffer->c_byte = file_buffer->size;
+
+               /*
+                * Specutively pull into the fragment cache any fragment blocks
+                * which contain fragments which *this* fragment may be
+                * be a duplicate.
+                *
+                * By ensuring the fragment block is in cache ahead of time
+                * should eliminate the parallelisation stall when the
+                * main thread needs to read the fragment block to do a
+                * duplicate check on it.
+                *
+                * If this is a fragment belonging to a larger file
+                * (with additional blocks) then ignore it.  Here we're
+                * interested in the "low hanging fruit" of files which
+                * consist of only a fragment
+                */
+               if(file_buffer->file_size != file_buffer->size) {
+                       seq_queue_put(to_main, file_buffer);
+                       continue;
+               }
+
+               file_size = file_buffer->file_size;
+
+               pthread_mutex_lock(&dup_mutex);
+               dupl_ptr = dupl[DUP_HASH(file_size)];
+               pthread_mutex_unlock(&dup_mutex);
+
+               file_buffer->dupl_start = dupl_ptr;
+               file_buffer->duplicate = FALSE;
+
+               for(; dupl_ptr; dupl_ptr = dupl_ptr->next) {
+                       if(file_size != dupl_ptr->file_size ||
+                                       file_size != dupl_ptr->fragment->size)
+                               continue;
+
+                       pthread_mutex_lock(&dup_mutex);
+                       flag = dupl_ptr->have_frag_checksum;
+                       checksum = dupl_ptr->fragment_checksum;
+                       pthread_mutex_unlock(&dup_mutex);
+
+                       /*
+                        * If we have the checksum and it matches then
+                        * read in the fragment block.
+                        *
+                        * If we *don't* have the checksum, then we are
+                        * appending, and the fragment block is on the
+                        * "old" filesystem.  Read it in and checksum
+                        * the entire fragment buffer
+                        */
+                       if(!flag) {
+                               buffer = get_fragment_cksum(dupl_ptr,
+                                       data_buffer, fd, &checksum);
+                               if(checksum != file_buffer->checksum) {
+                                       cache_block_put(buffer);
+                                       continue;
+                               }
+                       } else if(checksum == file_buffer->checksum)
+                               buffer = get_fragment(dupl_ptr->fragment,
+                                       data_buffer, fd);
+                       else
+                               continue;
+
+                       res = memcmp(file_buffer->data, buffer->data +
+                               dupl_ptr->fragment->offset, file_size);
+                       cache_block_put(buffer);
+                       if(res == 0) {
+                               struct file_buffer *dup = malloc(sizeof(*dup));
+                               if(dup == NULL)
+                                       MEM_ERROR();
+                               memcpy(dup, file_buffer, sizeof(*dup));
+                               cache_block_put(file_buffer);
+                               dup->dupl_start = dupl_ptr;
+                               dup->duplicate = TRUE;
+                               file_buffer = dup;
+                               break;
+                       }
+               }
+
+               seq_queue_put(to_main, file_buffer);
+       }
+
+       pthread_cleanup_pop(0);
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/process_fragments.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/process_fragments.h
new file mode 100644 (file)
index 0000000..6f6bdf2
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef PROCESS_FRAGMENTS_H
+#define PROCESS_FRAGMENTS_H
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2014
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * process_fragments.h
+ */
+
+#define DUP_HASH(a) (a & 0xffff)
+
+extern void *frag_thrd(void *);
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/progressbar.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/progressbar.c
new file mode 100644 (file)
index 0000000..987a45b
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2012, 2013, 2014
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * progressbar.c
+ */
+
+#include <pthread.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <stdlib.h>
+
+#include "error.h"
+
+#define FALSE 0
+#define TRUE 1
+
+/* flag whether progressbar display is enabled or not */
+int display_progress_bar = FALSE;
+
+/* flag whether the progress bar is temporarily disbled */
+int temp_disabled = FALSE;
+
+int rotate = 0;
+int cur_uncompressed = 0, estimated_uncompressed = 0;
+int columns;
+
+pthread_t progress_thread;
+pthread_mutex_t progress_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+
+static void sigwinch_handler()
+{
+       struct winsize winsize;
+
+       if(ioctl(1, TIOCGWINSZ, &winsize) == -1) {
+               if(isatty(STDOUT_FILENO))
+                       ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 "
+                               "columns\n");
+               columns = 80;
+       } else
+               columns = winsize.ws_col;
+}
+
+
+static void sigalrm_handler()
+{
+       rotate = (rotate + 1) % 4;
+}
+
+
+void inc_progress_bar()
+{
+       cur_uncompressed ++;
+}
+
+
+void dec_progress_bar(int count)
+{
+       cur_uncompressed -= count;
+}
+
+
+void progress_bar_size(int count)
+{
+       estimated_uncompressed += count;
+}
+
+
+static void progress_bar(long long current, long long max, int columns)
+{
+       char rotate_list[] = { '|', '/', '-', '\\' };
+       int max_digits, used, hashes, spaces;
+       static int tty = -1;
+
+       if(max == 0)
+               return;
+
+       max_digits = floor(log10(max)) + 1;
+       used = max_digits * 2 + 11;
+       hashes = (current * (columns - used)) / max;
+       spaces = columns - used - hashes;
+
+       if((current > max) || (columns - used < 0))
+               return;
+
+       if(tty == -1)
+               tty = isatty(STDOUT_FILENO);
+       if(!tty) {
+               static long long previous = -1;
+
+               /* Updating much more frequently than this results in huge
+                * log files. */
+               if((current % 100) != 0 && current != max)
+                       return;
+               /* Don't update just to rotate the spinner. */
+               if(current == previous)
+                       return;
+               previous = current;
+       }
+
+       printf("\r[");
+
+       while (hashes --)
+               putchar('=');
+
+       putchar(rotate_list[rotate]);
+
+       while(spaces --)
+               putchar(' ');
+
+       printf("] %*lld/%*lld", max_digits, current, max_digits, max);
+       printf(" %3lld%%", current * 100 / max);
+       fflush(stdout);
+}
+
+
+void enable_progress_bar()
+{
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
+       pthread_mutex_lock(&progress_mutex);
+       if(display_progress_bar)
+               progress_bar(cur_uncompressed, estimated_uncompressed, columns);
+       temp_disabled = FALSE;
+       pthread_cleanup_pop(1);
+}
+
+
+void disable_progress_bar()
+{
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
+       pthread_mutex_lock(&progress_mutex);
+       if(display_progress_bar)
+               printf("\n");
+       temp_disabled = TRUE;
+       pthread_cleanup_pop(1);
+}
+
+
+void set_progressbar_state(int state)
+{
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
+       pthread_mutex_lock(&progress_mutex);
+       if(display_progress_bar != state) {
+               if(display_progress_bar && !temp_disabled) {
+                       progress_bar(cur_uncompressed, estimated_uncompressed,
+                               columns);
+                       printf("\n");
+               }
+               display_progress_bar = state;
+       }
+       pthread_cleanup_pop(1);
+}
+
+
+void *progress_thrd(void *arg)
+{
+       struct timespec requested_time, remaining;
+       struct itimerval itimerval;
+       struct winsize winsize;
+
+       if(ioctl(1, TIOCGWINSZ, &winsize) == -1) {
+               if(isatty(STDOUT_FILENO))
+                       ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 "
+                               "columns\n");
+               columns = 80;
+       } else
+               columns = winsize.ws_col;
+       signal(SIGWINCH, sigwinch_handler);
+       signal(SIGALRM, sigalrm_handler);
+
+       itimerval.it_value.tv_sec = 0;
+       itimerval.it_value.tv_usec = 250000;
+       itimerval.it_interval.tv_sec = 0;
+       itimerval.it_interval.tv_usec = 250000;
+       setitimer(ITIMER_REAL, &itimerval, NULL);
+
+       requested_time.tv_sec = 0;
+       requested_time.tv_nsec = 250000000;
+
+       while(1) {
+               int res = nanosleep(&requested_time, &remaining);
+
+               if(res == -1 && errno != EINTR)
+                       BAD_ERROR("nanosleep failed in progress thread\n");
+
+               pthread_mutex_lock(&progress_mutex);
+               if(display_progress_bar && !temp_disabled)
+                       progress_bar(cur_uncompressed, estimated_uncompressed,
+                               columns);
+               pthread_mutex_unlock(&progress_mutex);
+       }
+}
+
+
+void init_progress_bar()
+{
+       pthread_create(&progress_thread, NULL, progress_thrd, NULL);
+}
+
+
+void progressbar_error(char *fmt, ...)
+{
+       va_list ap;
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
+       pthread_mutex_lock(&progress_mutex);
+
+       if(display_progress_bar && !temp_disabled)
+               fprintf(stderr, "\n");
+
+       va_start(ap, fmt);
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+
+       pthread_cleanup_pop(1);
+}
+
+
+void progressbar_info(char *fmt, ...)
+{
+       va_list ap;
+
+       pthread_cleanup_push((void *) pthread_mutex_unlock, &progress_mutex);
+       pthread_mutex_lock(&progress_mutex);
+
+       if(display_progress_bar && !temp_disabled)
+               printf("\n");
+
+       va_start(ap, fmt);
+       vprintf(fmt, ap);
+       va_end(ap);
+
+       pthread_cleanup_pop(1);
+}
+
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/progressbar.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/progressbar.h
new file mode 100644 (file)
index 0000000..b33b367
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef PROGRESSBAR_H
+#define PROGRESSBAR_H
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2012, 2013, 2014
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * progressbar.h
+ */
+
+extern void inc_progress_bar();
+extern void dec_progress_bar(int count);
+extern void progress_bar_size(int count);
+extern void enable_progress_bar();
+extern void disable_progress_bar();
+extern void init_progress_bar();
+extern void set_progressbar_state(int);
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/pseudo.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/pseudo.c
new file mode 100644 (file)
index 0000000..48e6b27
--- /dev/null
@@ -0,0 +1,559 @@
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2009, 2010, 2012, 2014, 2017, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * pseudo.c
+ */
+
+#include <pwd.h>
+#include <grp.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <sys/stat.h>
+#include <ctype.h>
+
+#include "pseudo.h"
+#include "error.h"
+#include "progressbar.h"
+
+#define TRUE 1
+#define FALSE 0
+
+extern int read_file(char *filename, char *type, int (parse_line)(char *));
+
+struct pseudo_dev **pseudo_file = NULL;
+struct pseudo *pseudo = NULL;
+int pseudo_count = 0;
+
+static char *get_component(char *target, char **targname)
+{
+       char *start;
+
+       start = target;
+       while(*target != '/' && *target != '\0')
+               target ++;
+
+       *targname = strndup(start, target - start);
+
+       while(*target == '/')
+               target ++;
+
+       return target;
+}
+
+
+/*
+ * Add pseudo device target to the set of pseudo devices.  Pseudo_dev
+ * describes the pseudo device attributes.
+ */
+struct pseudo *add_pseudo(struct pseudo *pseudo, struct pseudo_dev *pseudo_dev,
+       char *target, char *alltarget)
+{
+       char *targname;
+       int i;
+
+       target = get_component(target, &targname);
+
+       if(pseudo == NULL) {
+               pseudo = malloc(sizeof(struct pseudo));
+               if(pseudo == NULL)
+                       MEM_ERROR();
+
+               pseudo->names = 0;
+               pseudo->count = 0;
+               pseudo->name = NULL;
+       }
+
+       for(i = 0; i < pseudo->names; i++)
+               if(strcmp(pseudo->name[i].name, targname) == 0)
+                       break;
+
+       if(i == pseudo->names) {
+               /* allocate new name entry */
+               pseudo->names ++;
+               pseudo->name = realloc(pseudo->name, (i + 1) *
+                       sizeof(struct pseudo_entry));
+               if(pseudo->name == NULL)
+                       MEM_ERROR();
+               pseudo->name[i].name = targname;
+
+               if(target[0] == '\0') {
+                       /* at leaf pathname component */
+                       pseudo->name[i].pseudo = NULL;
+                       pseudo->name[i].pathname = strdup(alltarget);
+                       pseudo->name[i].dev = pseudo_dev;
+               } else {
+                       /* recurse adding child components */
+                       pseudo->name[i].dev = NULL;
+                       pseudo->name[i].pseudo = add_pseudo(NULL, pseudo_dev,
+                               target, alltarget);
+               }
+       } else {
+               /* existing matching entry */
+               free(targname);
+
+               if(pseudo->name[i].pseudo == NULL) {
+                       /* No sub-directory which means this is the leaf
+                        * component of a pre-existing pseudo file.
+                        */
+                       if(target[0] != '\0') {
+                               /*
+                                * entry must exist as either a 'd' type or
+                                * 'm' type pseudo file
+                                */
+                               if(pseudo->name[i].dev->type == 'd' ||
+                                       pseudo->name[i].dev->type == 'm')
+                                       /* recurse adding child components */
+                                       pseudo->name[i].pseudo =
+                                               add_pseudo(NULL, pseudo_dev,
+                                               target, alltarget);
+                               else {
+                                       ERROR_START("%s already exists as a "
+                                               "non directory.",
+                                               pseudo->name[i].name);
+                                       ERROR_EXIT(".  Ignoring %s!\n",
+                                               alltarget);
+                               }
+                       } else if(memcmp(pseudo_dev, pseudo->name[i].dev,
+                                       sizeof(struct pseudo_dev)) != 0) {
+                               ERROR_START("%s already exists as a different "
+                                       "pseudo definition.", alltarget);
+                               ERROR_EXIT("  Ignoring!\n");
+                       } else {
+                               ERROR_START("%s already exists as an identical "
+                                       "pseudo definition!", alltarget);
+                               ERROR_EXIT("  Ignoring!\n");
+                       }
+               } else {
+                       if(target[0] == '\0') {
+                               /*
+                                * sub-directory exists, which means we can only
+                                * add a pseudo file of type 'd' or type 'm'
+                                */
+                               if(pseudo->name[i].dev == NULL &&
+                                               (pseudo_dev->type == 'd' ||
+                                               pseudo_dev->type == 'm')) {
+                                       pseudo->name[i].pathname =
+                                               strdup(alltarget);
+                                       pseudo->name[i].dev = pseudo_dev;
+                               } else {
+                                       ERROR_START("%s already exists as a "
+                                               "different pseudo definition.",
+                                               pseudo->name[i].name);
+                                       ERROR_EXIT("  Ignoring %s!\n",
+                                               alltarget);
+                               }
+                       } else
+                               /* recurse adding child components */
+                               add_pseudo(pseudo->name[i].pseudo, pseudo_dev,
+                                       target, alltarget);
+               }
+       }
+
+       return pseudo;
+}
+
+
+/*
+ * Find subdirectory in pseudo directory referenced by pseudo, matching
+ * filename.  If filename doesn't exist or if filename is a leaf file
+ * return NULL
+ */
+struct pseudo *pseudo_subdir(char *filename, struct pseudo *pseudo)
+{
+       int i;
+
+       if(pseudo == NULL)
+               return NULL;
+
+       for(i = 0; i < pseudo->names; i++)
+               if(strcmp(filename, pseudo->name[i].name) == 0)
+                       return pseudo->name[i].pseudo;
+
+       return NULL;
+}
+
+
+struct pseudo_entry *pseudo_readdir(struct pseudo *pseudo)
+{
+       if(pseudo == NULL)
+               return NULL;
+
+       while(pseudo->count < pseudo->names) {
+               if(pseudo->name[pseudo->count].dev != NULL)
+                       return &pseudo->name[pseudo->count++];
+               else
+                       pseudo->count++;
+       }
+
+       return NULL;
+}
+
+
+int pseudo_exec_file(struct pseudo_dev *dev, int *child)
+{
+       int res, pipefd[2];
+
+       res = pipe(pipefd);
+       if(res == -1) {
+               ERROR("Executing dynamic pseudo file, pipe failed\n");
+               return 0;
+       }
+
+       *child = fork();
+       if(*child == -1) {
+               ERROR("Executing dynamic pseudo file, fork failed\n");
+               goto failed;
+       }
+
+       if(*child == 0) {
+               close(pipefd[0]);
+               close(STDOUT_FILENO);
+               res = dup(pipefd[1]);
+               if(res == -1)
+                       exit(EXIT_FAILURE);
+
+               execl("/bin/sh", "sh", "-c", dev->command, (char *) NULL);
+               exit(EXIT_FAILURE);
+       }
+
+       close(pipefd[1]);
+       return pipefd[0];
+
+failed:
+       close(pipefd[0]);
+       close(pipefd[1]);
+       return 0;
+}
+
+
+void add_pseudo_file(struct pseudo_dev *dev)
+{
+       pseudo_file = realloc(pseudo_file, (pseudo_count + 1) *
+               sizeof(struct pseudo_dev *));
+       if(pseudo_file == NULL)
+               MEM_ERROR();
+
+       dev->pseudo_id = pseudo_count;
+       pseudo_file[pseudo_count ++] = dev;
+}
+
+
+struct pseudo_dev *get_pseudo_file(int pseudo_id)
+{
+       return pseudo_file[pseudo_id];
+}
+
+
+int read_pseudo_def(char *def)
+{
+       int n, bytes;
+       int quoted = 0;
+       unsigned int major = 0, minor = 0, mode;
+       char type, *ptr;
+       char suid[100], sgid[100]; /* overflow safe */
+       char *filename, *name;
+       char *orig_def = def;
+       long long uid, gid;
+       struct pseudo_dev *dev;
+
+       /*
+        * Scan for filename, don't use sscanf() and "%s" because
+        * that can't handle filenames with spaces.
+        *
+        * Filenames with spaces should either escape (backslash) the
+        * space or use double quotes.
+        */
+       filename = malloc(strlen(def) + 1);
+       if(filename == NULL)
+               MEM_ERROR();
+
+       for(name = filename; (quoted || !isspace(*def)) && *def != '\0';) {
+               if(*def == '"') {
+                       quoted = !quoted;
+                       def ++;
+                       continue;
+               }
+
+               if(*def == '\\') {
+                       def ++;
+                       if (*def == '\0')
+                               break;
+               }
+               *name ++ = *def ++;
+       }
+       *name = '\0';
+
+       /* Skip any leading slashes (/) */
+       for(name = filename; *name == '/'; name ++);
+
+       if(*name == '\0') {
+               ERROR("Not enough or invalid arguments in pseudo file "
+                       "definition \"%s\"\n", orig_def);
+               goto error;
+       }
+
+       n = sscanf(def, " %c %o %99s %99s %n", &type, &mode, suid, sgid,
+               &bytes);
+       def += bytes;
+
+       if(n < 4) {
+               ERROR("Not enough or invalid arguments in pseudo file "
+                       "definition \"%s\"\n", orig_def);
+               switch(n) {
+               case -1:
+                       /* FALLTHROUGH */
+               case 0:
+                       /* FALLTHROUGH */
+               case 1:
+                       ERROR("Couldn't parse filename, type or octal mode\n");
+                       ERROR("If the filename has spaces, either quote it, or "
+                               "backslash the spaces\n");
+                       break;
+               case 2:
+                       ERROR("Read filename, type and mode, but failed to "
+                               "read or match uid\n");
+                       break;
+               default:
+                       ERROR("Read filename, type, mode and uid, but failed "
+                               "to read or match gid\n");
+                       break; 
+               }
+               goto error;
+       }
+
+       switch(type) {
+       case 'b':
+               /* FALLTHROUGH */
+       case 'c':
+               n = sscanf(def, "%u %u %n", &major, &minor, &bytes);
+               def += bytes;
+
+               if(n < 2) {
+                       ERROR("Not enough or invalid arguments in %s device "
+                               "pseudo file definition \"%s\"\n", type == 'b' ?
+                               "block" : "character", orig_def);
+                       if(n < 1)
+                               ERROR("Read filename, type, mode, uid and gid, "
+                                       "but failed to read or match major\n");
+                       else
+                               ERROR("Read filename, type, mode, uid, gid "
+                                       "and major, but failed to read  or "
+                                       "match minor\n");
+                       goto error;
+               }       
+               
+               if(major > 0xfff) {
+                       ERROR("Major %d out of range\n", major);
+                       goto error;
+               }
+
+               if(minor > 0xfffff) {
+                       ERROR("Minor %d out of range\n", minor);
+                       goto error;
+               }
+               /* FALLTHROUGH */
+       case 'd':
+               /* FALLTHROUGH */
+       case 'm':
+               /*
+                * Check for trailing junk after expected arguments
+                */
+               if(def[0] != '\0') {
+                       ERROR("Unexpected tailing characters in pseudo file "
+                               "definition \"%s\"\n", orig_def);
+                       goto error;
+               }
+               break;
+       case 'f':
+               if(def[0] == '\0') {
+                       ERROR("Not enough arguments in dynamic file pseudo "
+                               "definition \"%s\"\n", orig_def);
+                       ERROR("Expected command, which can be an executable "
+                               "or a piece of shell script\n");
+                       goto error;
+               }       
+               break;
+       case 's':
+               if(def[0] == '\0') {
+                       ERROR("Not enough arguments in symlink pseudo "
+                               "definition \"%s\"\n", orig_def);
+                       ERROR("Expected symlink\n");
+                       goto error;
+               }
+
+               if(strlen(def) > 65535) {
+                       ERROR("Symlink pseudo definition %s is greater than 65535"
+                                                               " bytes!\n", def);
+                       goto error;
+               }
+               break;
+       default:
+               ERROR("Unsupported type %c\n", type);
+               goto error;
+       }
+
+
+       if(mode > 07777) {
+               ERROR("Mode %o out of range\n", mode);
+               goto error;
+       }
+
+       uid = strtoll(suid, &ptr, 10);
+       if(*ptr == '\0') {
+               if(uid < 0 || uid > ((1LL << 32) - 1)) {
+                       ERROR("Uid %s out of range\n", suid);
+                       goto error;
+               }
+       } else {
+               struct passwd *pwuid = getpwnam(suid);
+               if(pwuid)
+                       uid = pwuid->pw_uid;
+               else {
+                       ERROR("Uid %s invalid uid or unknown user\n", suid);
+                       goto error;
+               }
+       }
+               
+       gid = strtoll(sgid, &ptr, 10);
+       if(*ptr == '\0') {
+               if(gid < 0 || gid > ((1LL << 32) - 1)) {
+                       ERROR("Gid %s out of range\n", sgid);
+                       goto error;
+               }
+       } else {
+               struct group *grgid = getgrnam(sgid);
+               if(grgid)
+                       gid = grgid->gr_gid;
+               else {
+                       ERROR("Gid %s invalid uid or unknown user\n", sgid);
+                       goto error;
+               }
+       }
+
+       switch(type) {
+       case 'b':
+               mode |= S_IFBLK;
+               break;
+       case 'c':
+               mode |= S_IFCHR;
+               break;
+       case 'd':
+               mode |= S_IFDIR;
+               break;
+       case 'f':
+               mode |= S_IFREG;
+               break;
+       case 's':
+               /* permissions on symlinks are always rwxrwxrwx */
+               mode = 0777 | S_IFLNK;
+               break;
+       }
+
+       dev = malloc(sizeof(struct pseudo_dev));
+       if(dev == NULL)
+               MEM_ERROR();
+
+       dev->type = type;
+       dev->mode = mode;
+       dev->uid = uid;
+       dev->gid = gid;
+       dev->major = major;
+       dev->minor = minor;
+       if(type == 'f') {
+               dev->command = strdup(def);
+               add_pseudo_file(dev);
+       }
+       if(type == 's')
+               dev->symlink = strdup(def);
+
+       pseudo = add_pseudo(pseudo, dev, name, name);
+
+       free(filename);
+       return TRUE;
+
+error:
+       ERROR("Pseudo definitions should be of the format\n");
+       ERROR("\tfilename d mode uid gid\n");
+       ERROR("\tfilename m mode uid gid\n");
+       ERROR("\tfilename b mode uid gid major minor\n");
+       ERROR("\tfilename c mode uid gid major minor\n");
+       ERROR("\tfilename f mode uid gid command\n");
+       ERROR("\tfilename s mode uid gid symlink\n");
+       free(filename);
+       return FALSE;
+}
+
+
+int read_pseudo_file(char *filename)
+{
+       return read_file(filename, "pseudo", read_pseudo_def);
+}
+
+
+struct pseudo *get_pseudo()
+{
+       return pseudo;
+}
+
+
+#ifdef SQUASHFS_TRACE
+static void dump_pseudo(struct pseudo *pseudo, char *string)
+{
+       int i, res;
+       char *path;
+
+       for(i = 0; i < pseudo->names; i++) {
+               struct pseudo_entry *entry = &pseudo->name[i];
+               if(string) {
+                       res = asprintf(&path, "%s/%s", string, entry->name);
+                       if(res == -1)
+                               BAD_ERROR("asprintf failed in dump_pseudo\n");
+               } else
+                       path = entry->name;
+               if(entry->dev)
+                       ERROR("%s %c 0%o %d %d %d %d\n", path, entry->dev->type,
+                               entry->dev->mode & ~S_IFMT, entry->dev->uid,
+                               entry->dev->gid, entry->dev->major,
+                               entry->dev->minor);
+               if(entry->pseudo)
+                       dump_pseudo(entry->pseudo, path);
+               if(string)
+                       free(path);
+       }
+}
+
+
+void dump_pseudos()
+{
+    if (pseudo)
+        dump_pseudo(pseudo, NULL);
+}
+#else
+void dump_pseudos()
+{
+}
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/pseudo.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/pseudo.h
new file mode 100644 (file)
index 0000000..ef693c3
--- /dev/null
@@ -0,0 +1,61 @@
+#ifndef PSEUDO_H
+#define PSEUDO_H
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2009, 2010, 2014
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * pseudo.h
+ */
+struct pseudo_dev {
+       char            type;
+       unsigned int    mode;
+       unsigned int    uid;
+       unsigned int    gid;
+       unsigned int    major;
+       unsigned int    minor;
+       int             pseudo_id;
+       union {
+               char            *command;
+               char            *symlink;
+       };
+};
+
+struct pseudo_entry {
+       char                    *name;
+       char                    *pathname;
+       struct pseudo           *pseudo;
+       struct pseudo_dev       *dev;
+};
+       
+struct pseudo {
+       int                     names;
+       int                     count;
+       struct pseudo_entry     *name;
+};
+
+extern int read_pseudo_def(char *);
+extern int read_pseudo_file(char *);
+extern struct pseudo *pseudo_subdir(char *, struct pseudo *);
+extern struct pseudo_entry *pseudo_readdir(struct pseudo *);
+extern struct pseudo_dev *get_pseudo_file(int);
+extern int pseudo_exec_file(struct pseudo_dev *, int *);
+extern struct pseudo *get_pseudo();
+extern void dump_pseudos();
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/read_file.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/read_file.c
new file mode 100644 (file)
index 0000000..22705a0
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2012
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * read_file.c
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+
+#include "error.h"
+
+#define TRUE 1
+#define FALSE 0
+#define MAX_LINE 16384
+
+/*
+ * Read file, passing each line to parse_line() for
+ * parsing.
+ *
+ * Lines can be split across multiple lines using "\".
+ * 
+ * Blank lines and comment lines indicated by # are supported.
+ */
+int read_file(char *filename, char *type, int (parse_line)(char *))
+{
+       FILE *fd;
+       char *def, *err, *line = NULL;
+       int res, size = 0;
+
+       fd = fopen(filename, "r");
+       if(fd == NULL) {
+               ERROR("Could not open %s device file \"%s\" because %s\n",
+                       type, filename, strerror(errno));
+               return FALSE;
+       }
+
+       while(1) {
+               int total = 0;
+
+               while(1) {
+                       int len;
+
+                       if(total + (MAX_LINE + 1) > size) {
+                               line = realloc(line, size += (MAX_LINE + 1));
+                               if(line == NULL)
+                                       MEM_ERROR();
+                       }
+
+                       err = fgets(line + total, MAX_LINE + 1, fd);
+                       if(err == NULL)
+                               break;
+
+                       len = strlen(line + total);
+                       total += len;
+
+                       if(len == MAX_LINE && line[total - 1] != '\n') {
+                               /* line too large */
+                               ERROR("Line too long when reading "
+                                       "%s file \"%s\", larger than "
+                                       "%d bytes\n", type, filename, MAX_LINE);
+                               goto failed;
+                       }
+
+                       /*
+                        * Remove '\n' terminator if it exists (the last line
+                        * in the file may not be '\n' terminated)
+                        */
+                       if(len && line[total - 1] == '\n') {
+                               line[-- total] = '\0';
+                               len --;
+                       }
+
+                       /*
+                        * If no line continuation then jump out to
+                        * process line.  Note, we have to be careful to
+                        * check for "\\" (backslashed backslash) and to
+                        * ensure we don't look at the previous line
+                        */
+                       if(len == 0 || line[total - 1] != '\\' || (len >= 2 &&
+                                       strcmp(line + total - 2, "\\\\") == 0))
+                               break;
+                       else
+                               total --;
+               }       
+
+               if(err == NULL) {
+                       if(ferror(fd)) {
+                               ERROR("Reading %s file \"%s\" failed "
+                                       "because %s\n", type, filename,
+                                       strerror(errno));
+                               goto failed;
+                       }
+
+                       /*
+                        * At EOF, normally we'll be finished, but, have to
+                        * check for special case where we had "\" line
+                        * continuation and then hit EOF immediately afterwards
+                        */
+                       if(total == 0)
+                               break;
+                       else
+                               line[total] = '\0';
+               }
+
+               /* Skip any leading whitespace */
+               for(def = line; isspace(*def); def ++);
+
+               /* if line is now empty after skipping characters, skip it */
+               if(*def == '\0')
+                       continue;
+
+               /* if comment line, skip */
+               if(*def == '#')
+                       continue;
+
+               res = parse_line(def);
+               if(res == FALSE)
+                       goto failed;
+       }
+
+       fclose(fd);
+       free(line);
+       return TRUE;
+
+failed:
+       fclose(fd);
+       free(line);
+       return FALSE;
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/read_fs.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/read_fs.c
new file mode 100644 (file)
index 0000000..0730e8c
--- /dev/null
@@ -0,0 +1,996 @@
+/*
+ * Read a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010,
+ * 2012, 2013, 2014, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * read_fs.c
+ */
+
+#define TRUE 1
+#define FALSE 0
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <limits.h>
+#include <dirent.h>
+
+#ifndef linux
+#define __BYTE_ORDER BYTE_ORDER
+#define __BIG_ENDIAN BIG_ENDIAN
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#else
+#include <endian.h>
+#endif
+
+#include <stdlib.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_swap.h"
+#include "compressor.h"
+#include "xattr.h"
+#include "error.h"
+#include "mksquashfs.h"
+
+int read_block(int fd, long long start, long long *next, int expected,
+                                                               void *block)
+{
+       unsigned short c_byte;
+       int res, compressed;
+       int outlen = expected ? expected : SQUASHFS_METADATA_SIZE;
+       
+       /* Read block size */
+       res = read_fs_bytes(fd, start, 2, &c_byte);
+       if(res == 0)
+               return 0;
+
+       SQUASHFS_INSWAP_SHORTS(&c_byte, 1);
+       compressed = SQUASHFS_COMPRESSED(c_byte);
+       c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
+
+       /*
+        * The block size should not be larger than
+        * the uncompressed size (or max uncompressed size if
+        * expected is 0)
+        */
+       if (c_byte > outlen)
+               return 0;
+
+       if(compressed) {
+               char buffer[c_byte];
+               int error;
+
+               res = read_fs_bytes(fd, start + 2, c_byte, buffer);
+               if(res == 0)
+                       return 0;
+
+               res = compressor_uncompress(comp, block, buffer, c_byte,
+                       outlen, &error);
+               if(res == -1) {
+                       ERROR("%s uncompress failed with error code %d\n",
+                               comp->name, error);
+                       return 0;
+               }
+       } else {
+               res = read_fs_bytes(fd, start + 2, c_byte, block);
+               if(res == 0)
+                       return 0;
+               res = c_byte;
+       }
+
+       if(next)
+               *next = start + 2 + c_byte;
+
+       /*
+        * if expected, then check the (uncompressed) return data
+        * is of the expected size
+        */
+       if(expected && expected != res)
+               return 0;
+       else
+               return res;
+}
+
+
+#define NO_BYTES(SIZE) \
+       (bytes - (cur_ptr - inode_table) < (SIZE))
+
+#define NO_INODE_BYTES(INODE) NO_BYTES(sizeof(struct INODE))
+
+unsigned char *scan_inode_table(int fd, long long start, long long end,
+       long long root_inode_start, int root_inode_offset,
+       struct squashfs_super_block *sBlk, union squashfs_inode_header
+       *dir_inode, unsigned int *root_inode_block, unsigned int
+       *root_inode_size, long long *uncompressed_file, unsigned int
+       *uncompressed_directory, int *file_count, int *sym_count, int
+       *dev_count, int *dir_count, int *fifo_count, int *sock_count,
+       unsigned int *id_table)
+{
+       unsigned char *cur_ptr;
+       unsigned char *inode_table = NULL;
+       int byte, files = 0;
+       unsigned int directory_start_block, bytes = 0, size = 0;
+       struct squashfs_base_inode_header base;
+
+       TRACE("scan_inode_table: start 0x%llx, end 0x%llx, root_inode_start "
+               "0x%llx\n", start, end, root_inode_start);
+
+       *root_inode_block = UINT_MAX;
+       while(start < end) {
+               if(start == root_inode_start) {
+                       TRACE("scan_inode_table: read compressed block 0x%llx "
+                               "containing root inode\n", start);
+                       *root_inode_block = bytes;
+               }
+               if(size - bytes < SQUASHFS_METADATA_SIZE) {
+                       inode_table = realloc(inode_table, size
+                               += SQUASHFS_METADATA_SIZE);
+                       if(inode_table == NULL)
+                               MEM_ERROR();
+               }
+               TRACE("scan_inode_table: reading block 0x%llx\n", start);
+               byte = read_block(fd, start, &start, 0, inode_table + bytes);
+               if(byte == 0)
+                       goto corrupted;
+
+               bytes += byte;
+
+               /* If this is not the last metadata block in the inode table
+                * then it should be SQUASHFS_METADATA_SIZE in size.
+                * Note, we can't use expected in read_block() above for this
+                * because we don't know if this is the last block until
+                * after reading.
+                */
+               if(start != end && byte != SQUASHFS_METADATA_SIZE)
+                       goto corrupted;
+       }
+
+       /*
+        * We expect to have found the metadata block containing the
+        * root inode in the above inode_table metadata block scan.  If it
+        * hasn't been found then the filesystem is corrupted
+        */
+       if(*root_inode_block == UINT_MAX)
+               goto corrupted;
+
+       /*
+        * The number of bytes available after the root inode medata block
+        * should be at least the root inode offset + the size of a
+        * regular directory inode, if not the filesystem is corrupted
+        *
+        *      +-----------------------+-----------------------+
+        *      |                       |        directory      |
+        *      |                       |          inode        |
+        *      +-----------------------+-----------------------+
+        *      ^                       ^                       ^
+        *      *root_inode_block       root_inode_offset       bytes
+        */
+       if((bytes - *root_inode_block) < (root_inode_offset +
+                       sizeof(struct squashfs_dir_inode_header)))
+               goto corrupted;
+
+       /*
+        * Read last inode entry which is the root directory inode, and obtain
+        * the last directory start block index.  This is used when calculating
+        * the total uncompressed directory size.  The directory bytes in the
+        * last * block will be counted as normal.
+        *
+        * Note, the previous check ensures the following calculation won't
+        * underflow, and we won't access beyond the buffer
+        */
+       *root_inode_size = bytes - (*root_inode_block + root_inode_offset);
+       bytes = *root_inode_block + root_inode_offset;
+       SQUASHFS_SWAP_DIR_INODE_HEADER(inode_table + bytes, &dir_inode->dir);
+       
+       if(dir_inode->base.inode_type == SQUASHFS_DIR_TYPE)
+               directory_start_block = dir_inode->dir.start_block;
+       else if(dir_inode->base.inode_type == SQUASHFS_LDIR_TYPE) {
+               if(*root_inode_size < sizeof(struct squashfs_ldir_inode_header))
+                       /* corrupted filesystem */
+                       goto corrupted;
+               SQUASHFS_SWAP_LDIR_INODE_HEADER(inode_table + bytes,
+                       &dir_inode->ldir);
+               directory_start_block = dir_inode->ldir.start_block;
+       } else
+               /* bad type, corrupted filesystem */
+               goto corrupted;
+
+       get_uid(id_table[dir_inode->base.uid]);
+       get_guid(id_table[dir_inode->base.guid]);
+
+       /* allocate fragment to file mapping table */
+       file_mapping = calloc(sBlk->fragments, sizeof(struct append_file *));
+       if(file_mapping == NULL)
+               MEM_ERROR();
+
+       for(cur_ptr = inode_table; cur_ptr < inode_table + bytes; files ++) {
+               if(NO_INODE_BYTES(squashfs_base_inode_header))
+                       /* corrupted filesystem */
+                       goto corrupted;
+
+               SQUASHFS_SWAP_BASE_INODE_HEADER(cur_ptr, &base);
+
+               TRACE("scan_inode_table: processing inode @ byte position "
+                       "0x%x, type 0x%x\n",
+                       (unsigned int) (cur_ptr - inode_table),
+                       base.inode_type);
+
+               get_uid(id_table[base.uid]);
+               get_guid(id_table[base.guid]);
+
+               switch(base.inode_type) {
+               case SQUASHFS_FILE_TYPE: {
+                       struct squashfs_reg_inode_header inode;
+                       int frag_bytes, blocks, i;
+                       long long start, file_bytes = 0;
+                       unsigned int *block_list;
+
+                       if(NO_INODE_BYTES(squashfs_reg_inode_header))
+                               /* corrupted filesystem */
+                               goto corrupted;
+
+                       SQUASHFS_SWAP_REG_INODE_HEADER(cur_ptr, &inode);
+
+                       frag_bytes = inode.fragment == SQUASHFS_INVALID_FRAG ?
+                               0 : inode.file_size % sBlk->block_size;
+                       blocks = inode.fragment == SQUASHFS_INVALID_FRAG ?
+                               (inode.file_size + sBlk->block_size - 1) >>
+                               sBlk->block_log : inode.file_size >>
+                               sBlk->block_log;
+                       start = inode.start_block;
+
+                       TRACE("scan_inode_table: regular file, file_size %d, "
+                               "blocks %d\n", inode.file_size, blocks);
+
+                       if(NO_BYTES(blocks * sizeof(unsigned int)))
+                               /* corrupted filesystem */
+                               goto corrupted;
+
+                       block_list = malloc(blocks * sizeof(unsigned int));
+                       if(block_list == NULL)
+                               MEM_ERROR();
+
+                       cur_ptr += sizeof(inode);
+                       SQUASHFS_SWAP_INTS(cur_ptr, block_list, blocks);
+
+                       *uncompressed_file += inode.file_size;
+                       (*file_count) ++;
+
+                       for(i = 0; i < blocks; i++)
+                               file_bytes +=
+                                       SQUASHFS_COMPRESSED_SIZE_BLOCK
+                                                               (block_list[i]);
+
+                       if(inode.fragment != SQUASHFS_INVALID_FRAG &&
+                                       inode.fragment >= sBlk->fragments) {
+                               free(block_list);
+                               goto corrupted;
+                       }
+
+                       add_file(start, inode.file_size, file_bytes,
+                               block_list, blocks, inode.fragment,
+                               inode.offset, frag_bytes);
+                               
+                       cur_ptr += blocks * sizeof(unsigned int);
+                       break;
+               }       
+               case SQUASHFS_LREG_TYPE: {
+                       struct squashfs_lreg_inode_header inode;
+                       int frag_bytes, blocks, i;
+                       long long start, file_bytes = 0;
+                       unsigned int *block_list;
+
+                       if(NO_INODE_BYTES(squashfs_lreg_inode_header))
+                               /* corrupted filesystem */
+                               goto corrupted;
+
+                       SQUASHFS_SWAP_LREG_INODE_HEADER(cur_ptr, &inode);
+
+                       frag_bytes = inode.fragment == SQUASHFS_INVALID_FRAG ?
+                               0 : inode.file_size % sBlk->block_size;
+                       blocks = inode.fragment == SQUASHFS_INVALID_FRAG ?
+                               (inode.file_size + sBlk->block_size - 1) >>
+                               sBlk->block_log : inode.file_size >>
+                               sBlk->block_log;
+                       start = inode.start_block;
+
+                       TRACE("scan_inode_table: extended regular "
+                               "file, file_size %lld, blocks %d\n",
+                               inode.file_size, blocks);
+
+                       if(NO_BYTES(blocks * sizeof(unsigned int)))
+                               /* corrupted filesystem */
+                               goto corrupted;
+
+                       block_list = malloc(blocks * sizeof(unsigned int));
+                       if(block_list == NULL)
+                               MEM_ERROR();
+
+                       cur_ptr += sizeof(inode);
+                       SQUASHFS_SWAP_INTS(cur_ptr, block_list, blocks);
+
+                       *uncompressed_file += inode.file_size;
+                       (*file_count) ++;
+
+                       for(i = 0; i < blocks; i++)
+                               file_bytes +=
+                                       SQUASHFS_COMPRESSED_SIZE_BLOCK
+                                                               (block_list[i]);
+
+                       if(inode.fragment != SQUASHFS_INVALID_FRAG &&
+                                       inode.fragment >= sBlk->fragments) {
+                               free(block_list);
+                               goto corrupted;
+                       }
+
+                       add_file(start, inode.file_size, file_bytes,
+                               block_list, blocks, inode.fragment,
+                               inode.offset, frag_bytes);
+
+                       cur_ptr += blocks * sizeof(unsigned int);
+                       break;
+               }       
+               case SQUASHFS_SYMLINK_TYPE:
+               case SQUASHFS_LSYMLINK_TYPE: {
+                       struct squashfs_symlink_inode_header inode;
+
+                       if(NO_INODE_BYTES(squashfs_symlink_inode_header))
+                               /* corrupted filesystem */
+                               goto corrupted;
+
+                       SQUASHFS_SWAP_SYMLINK_INODE_HEADER(cur_ptr, &inode);
+
+                       (*sym_count) ++;
+
+                       if (inode.inode_type == SQUASHFS_LSYMLINK_TYPE) {
+                               if(NO_BYTES(inode.symlink_size +
+                                                       sizeof(unsigned int)))
+                                       /* corrupted filesystem */
+                                       goto corrupted;
+                               cur_ptr += sizeof(inode) + inode.symlink_size +
+                                                       sizeof(unsigned int);
+                       } else {
+                               if(NO_BYTES(inode.symlink_size))
+                                       /* corrupted filesystem */
+                                       goto corrupted;
+                               cur_ptr += sizeof(inode) + inode.symlink_size;
+                       }
+                       break;
+               }
+               case SQUASHFS_DIR_TYPE: {
+                       struct squashfs_dir_inode_header dir_inode;
+
+                       if(NO_INODE_BYTES(squashfs_dir_inode_header))
+                               /* corrupted filesystem */
+                               goto corrupted;
+                               
+                       SQUASHFS_SWAP_DIR_INODE_HEADER(cur_ptr, &dir_inode);
+
+                       if(dir_inode.start_block < directory_start_block)
+                               *uncompressed_directory += dir_inode.file_size;
+
+                       (*dir_count) ++;
+                       cur_ptr += sizeof(struct squashfs_dir_inode_header);
+                       break;
+               }
+               case SQUASHFS_LDIR_TYPE: {
+                       struct squashfs_ldir_inode_header dir_inode;
+                       int i;
+
+                       if(NO_INODE_BYTES(squashfs_ldir_inode_header))
+                               /* corrupted filesystem */
+                               goto corrupted;
+
+                       SQUASHFS_SWAP_LDIR_INODE_HEADER(cur_ptr, &dir_inode);
+
+                       if(dir_inode.start_block < directory_start_block)
+                               *uncompressed_directory += dir_inode.file_size;
+
+                       (*dir_count) ++;
+                       cur_ptr += sizeof(struct squashfs_ldir_inode_header);
+
+                       for(i = 0; i < dir_inode.i_count; i++) {
+                               struct squashfs_dir_index index;
+
+                               if(NO_BYTES(sizeof(index)))
+                                       /* corrupted filesystem */
+                                       goto corrupted;
+                       
+                               SQUASHFS_SWAP_DIR_INDEX(cur_ptr, &index);
+
+                               if(NO_BYTES(index.size + 1))
+                                       /* corrupted filesystem */
+                                       goto corrupted;
+
+                               cur_ptr += sizeof(index) + index.size + 1;
+                       }
+                       break;
+               }
+               case SQUASHFS_BLKDEV_TYPE:
+               case SQUASHFS_CHRDEV_TYPE:
+                       if(NO_INODE_BYTES(squashfs_dev_inode_header))
+                               /* corrupted filesystem */
+                               goto corrupted;
+
+                       (*dev_count) ++;
+                       cur_ptr += sizeof(struct squashfs_dev_inode_header);
+                       break;
+               case SQUASHFS_LBLKDEV_TYPE:
+               case SQUASHFS_LCHRDEV_TYPE:
+                       if(NO_INODE_BYTES(squashfs_ldev_inode_header))
+                               /* corrupted filesystem */
+                               goto corrupted;
+
+                       (*dev_count) ++;
+                       cur_ptr += sizeof(struct squashfs_ldev_inode_header);
+                       break;
+               case SQUASHFS_FIFO_TYPE:
+                       if(NO_INODE_BYTES(squashfs_ipc_inode_header))
+                               /* corrupted filesystem */
+                               goto corrupted;
+
+                       (*fifo_count) ++;
+                       cur_ptr += sizeof(struct squashfs_ipc_inode_header);
+                       break;
+               case SQUASHFS_LFIFO_TYPE:
+                       if(NO_INODE_BYTES(squashfs_lipc_inode_header))
+                               /* corrupted filesystem */
+                               goto corrupted;
+
+                       (*fifo_count) ++;
+                       cur_ptr += sizeof(struct squashfs_lipc_inode_header);
+                       break;
+               case SQUASHFS_SOCKET_TYPE:
+                       if(NO_INODE_BYTES(squashfs_ipc_inode_header))
+                               /* corrupted filesystem */
+                               goto corrupted;
+
+                       (*sock_count) ++;
+                       cur_ptr += sizeof(struct squashfs_ipc_inode_header);
+                       break;
+               case SQUASHFS_LSOCKET_TYPE:
+                       if(NO_INODE_BYTES(squashfs_lipc_inode_header))
+                               /* corrupted filesystem */
+                               goto corrupted;
+
+                       (*sock_count) ++;
+                       cur_ptr += sizeof(struct squashfs_lipc_inode_header);
+                       break;
+               default:
+                       ERROR("Unknown inode type %d in scan_inode_table!\n",
+                                       base.inode_type);
+                       goto corrupted;
+               }
+       }
+       
+       printf("Read existing filesystem, %d inodes scanned\n", files);
+       return inode_table;
+
+corrupted:
+       ERROR("scan_inode_table: filesystem corruption detected in "
+               "scanning metadata\n");
+       free(inode_table);
+       return NULL;
+}
+
+
+struct compressor *read_super(int fd, struct squashfs_super_block *sBlk, char *source)
+{
+       int res, bytes = 0;
+       char buffer[SQUASHFS_METADATA_SIZE] __attribute__ ((aligned));
+
+       res = read_fs_bytes(fd, SQUASHFS_START, sizeof(struct squashfs_super_block),
+               sBlk);
+       if(res == 0) {
+               ERROR("Can't find a SQUASHFS superblock on %s\n",
+                               source);
+               ERROR("Wrong filesystem or filesystem is corrupted!\n");
+               goto failed_mount;
+       }
+
+       SQUASHFS_INSWAP_SUPER_BLOCK(sBlk);
+
+       if(sBlk->s_magic != SQUASHFS_MAGIC) {
+               if(sBlk->s_magic == SQUASHFS_MAGIC_SWAP)
+                       ERROR("Pre 4.0 big-endian filesystem on %s, appending"
+                               " to this is unsupported\n", source);
+               else {
+                       ERROR("Can't find a SQUASHFS superblock on %s\n",
+                               source);
+                       ERROR("Wrong filesystem or filesystem is corrupted!\n");
+               }
+               goto failed_mount;
+       }
+
+       /* Check the MAJOR & MINOR versions */
+       if(sBlk->s_major != SQUASHFS_MAJOR || sBlk->s_minor > SQUASHFS_MINOR) {
+               if(sBlk->s_major < 4)
+                       ERROR("Filesystem on %s is a SQUASHFS %d.%d filesystem."
+                               "  Appending\nto SQUASHFS %d.%d filesystems is "
+                               "not supported.  Please convert it to a "
+                               "SQUASHFS 4 filesystem\n", source,
+                               sBlk->s_major,
+                               sBlk->s_minor, sBlk->s_major, sBlk->s_minor);
+               else
+                       ERROR("Filesystem on %s is %d.%d, which is a later "
+                               "filesystem version than I support\n",
+                               source, sBlk->s_major, sBlk->s_minor);
+               goto failed_mount;
+       }
+
+       /* Check the compression type */
+       comp = lookup_compressor_id(sBlk->compression);
+       if(!comp->supported) {
+               ERROR("Filesystem on %s uses %s compression, this is "
+                       "unsupported by this version\n", source, comp->name);
+               ERROR("Compressors available:\n");
+               display_compressors("", "");
+               goto failed_mount;
+       }
+
+       /*
+        * Read extended superblock information from disk.
+        *
+        * Read compressor specific options from disk if present, and pass
+        * to compressor to set compressor options.
+        *
+        * Note, if there's no compressor options present, the compressor
+        * is still called to set the default options (the defaults may have
+        * been changed by the user specifying options on the command
+        * line which need to be over-ridden).
+        *
+        * Compressor_extract_options is also used to ensure that 
+        * we know how decompress a filesystem compressed with these
+        * compression options.
+        */
+       if(SQUASHFS_COMP_OPTS(sBlk->flags)) {
+               bytes = read_block(fd, sizeof(*sBlk), NULL, 0, buffer);
+
+               if(bytes == 0) {
+                       ERROR("Failed to read compressor options from append "
+                               "filesystem\n");
+                       ERROR("Filesystem corrupted?\n");
+                       goto failed_mount;
+               }
+       }
+
+       res = compressor_extract_options(comp, sBlk->block_size, buffer, bytes);
+       if(res == -1) {
+               ERROR("Compressor failed to set compressor options\n");
+               goto failed_mount;
+       }
+
+       printf("Found a valid %sSQUASHFS superblock on %s.\n",
+               SQUASHFS_EXPORTABLE(sBlk->flags) ? "exportable " : "", source);
+       printf("\tCompression used %s\n", comp->name);
+       printf("\tInodes are %scompressed\n",
+               SQUASHFS_UNCOMPRESSED_INODES(sBlk->flags) ? "un" : "");
+       printf("\tData is %scompressed\n",
+               SQUASHFS_UNCOMPRESSED_DATA(sBlk->flags) ? "un" : "");
+       printf("\tFragments are %scompressed\n",
+               SQUASHFS_UNCOMPRESSED_FRAGMENTS(sBlk->flags) ? "un" : "");
+       printf("\tXattrs are %scompressed\n",
+               SQUASHFS_UNCOMPRESSED_XATTRS(sBlk->flags) ? "un" : "");
+       printf("\tFragments are %spresent in the filesystem\n",
+               SQUASHFS_NO_FRAGMENTS(sBlk->flags) ? "not " : "");
+       printf("\tAlways-use-fragments option is %sspecified\n",
+               SQUASHFS_ALWAYS_FRAGMENTS(sBlk->flags) ? "" : "not ");
+       printf("\tDuplicates are %sremoved\n",
+               SQUASHFS_DUPLICATES(sBlk->flags) ? "" : "not ");
+       printf("\tXattrs are %sstored\n",
+               SQUASHFS_NO_XATTRS(sBlk->flags) ? "not " : "");
+       printf("\tFilesystem size %.2f Kbytes (%.2f Mbytes)\n",
+               sBlk->bytes_used / 1024.0, sBlk->bytes_used
+               / (1024.0 * 1024.0));
+       printf("\tBlock size %d\n", sBlk->block_size);
+       printf("\tNumber of fragments %d\n", sBlk->fragments);
+       printf("\tNumber of inodes %d\n", sBlk->inodes);
+       printf("\tNumber of ids %d\n", sBlk->no_ids);
+       TRACE("sBlk->inode_table_start %llx\n", sBlk->inode_table_start);
+       TRACE("sBlk->directory_table_start %llx\n",
+               sBlk->directory_table_start);
+       TRACE("sBlk->id_table_start %llx\n", sBlk->id_table_start);
+       TRACE("sBlk->fragment_table_start %llx\n", sBlk->fragment_table_start);
+       TRACE("sBlk->lookup_table_start %llx\n", sBlk->lookup_table_start);
+       TRACE("sBlk->xattr_id_table_start %llx\n", sBlk->xattr_id_table_start);
+       printf("\n");
+
+       return comp;
+
+failed_mount:
+       return NULL;
+}
+
+
+unsigned char *squashfs_readdir(int fd, int root_entries,
+       unsigned int directory_start_block, int offset, int size,
+       unsigned int *last_directory_block, struct squashfs_super_block *sBlk,
+       void (push_directory_entry)(char *, squashfs_inode, int, int))
+{
+       struct squashfs_dir_header dirh;
+       char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1]
+               __attribute__ ((aligned));
+       struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer;
+       unsigned char *directory_table = NULL;
+       int byte, bytes = 0, dir_count;
+       long long start = sBlk->directory_table_start + directory_start_block,
+               last_start_block = start; 
+
+       size += offset;
+       directory_table = malloc((size + SQUASHFS_METADATA_SIZE * 2 - 1) &
+               ~(SQUASHFS_METADATA_SIZE - 1));
+       if(directory_table == NULL)
+               MEM_ERROR();
+
+       while(bytes < size) {
+               int expected = (size - bytes) >= SQUASHFS_METADATA_SIZE ?
+                       SQUASHFS_METADATA_SIZE : 0;
+
+               TRACE("squashfs_readdir: reading block 0x%llx, bytes read so "
+                       "far %d\n", start, bytes);
+
+               last_start_block = start;
+               byte = read_block(fd, start, &start, expected, directory_table + bytes);
+               if(byte == 0) {
+                       ERROR("Failed to read directory\n");
+                       ERROR("Filesystem corrupted?\n");
+                       free(directory_table);
+                       return NULL;
+               }
+               bytes += byte;
+       }
+
+       if(!root_entries)
+               goto all_done;
+
+       bytes = offset;
+       while(bytes < size) {                   
+               SQUASHFS_SWAP_DIR_HEADER(directory_table + bytes, &dirh);
+
+               dir_count = dirh.count + 1;
+
+               /* dir_count should never be larger than SQUASHFS_DIR_COUNT */
+               if(dir_count > SQUASHFS_DIR_COUNT) {
+                       ERROR("File system corrupted: too many entries in directory\n");
+                       free(directory_table);
+                       return NULL;
+               }
+
+               TRACE("squashfs_readdir: Read directory header @ byte position "
+                       "0x%x, 0x%x directory entries\n", bytes, dir_count);
+               bytes += sizeof(dirh);
+
+               while(dir_count--) {
+                       SQUASHFS_SWAP_DIR_ENTRY(directory_table + bytes, dire);
+                       bytes += sizeof(*dire);
+
+                       /* size should never be SQUASHFS_NAME_LEN or larger */
+                       if(dire->size >= SQUASHFS_NAME_LEN) {
+                               ERROR("File system corrupted: filename too long\n");
+                               free(directory_table);
+                               return NULL;
+                       }
+
+                       memcpy(dire->name, directory_table + bytes,
+                               dire->size + 1);
+                       dire->name[dire->size + 1] = '\0';
+                       TRACE("squashfs_readdir: pushing directory entry %s, "
+                               "inode %x:%x, type 0x%x\n", dire->name,
+                               dirh.start_block, dire->offset, dire->type);
+                       push_directory_entry(dire->name,
+                               SQUASHFS_MKINODE(dirh.start_block,
+                               dire->offset), dirh.inode_number +
+                               dire->inode_number, dire->type);
+                       bytes += dire->size + 1;
+               }
+       }
+
+all_done:
+       *last_directory_block = (unsigned int) last_start_block -
+               sBlk->directory_table_start;
+       return directory_table;
+}
+
+
+unsigned int *read_id_table(int fd, struct squashfs_super_block *sBlk)
+{
+       int indexes = SQUASHFS_ID_BLOCKS(sBlk->no_ids);
+       long long index[indexes];
+       int bytes = SQUASHFS_ID_BYTES(sBlk->no_ids);
+       unsigned int *id_table;
+       int res, i;
+
+       id_table = malloc(bytes);
+       if(id_table == NULL)
+               MEM_ERROR();
+
+       res = read_fs_bytes(fd, sBlk->id_table_start,
+               SQUASHFS_ID_BLOCK_BYTES(sBlk->no_ids), index);
+       if(res == 0) {
+               ERROR("Failed to read id table index\n");
+               ERROR("Filesystem corrupted?\n");
+               free(id_table);
+               return NULL;
+       }
+
+       SQUASHFS_INSWAP_ID_BLOCKS(index, indexes);
+
+       for(i = 0; i < indexes; i++) {
+               int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
+                                       bytes & (SQUASHFS_METADATA_SIZE - 1);
+               int length = read_block(fd, index[i], NULL, expected,
+                       ((unsigned char *) id_table) +
+                       (i * SQUASHFS_METADATA_SIZE));
+               TRACE("Read id table block %d, from 0x%llx, length %d\n", i,
+                       index[i], length);
+               if(length == 0) {
+                       ERROR("Failed to read id table block %d, from 0x%llx, "
+                               "length %d\n", i, index[i], length);
+                       ERROR("Filesystem corrupted?\n");
+                       free(id_table);
+                       return NULL;
+               }
+       }
+
+       SQUASHFS_INSWAP_INTS(id_table, sBlk->no_ids);
+
+       for(i = 0; i < sBlk->no_ids; i++) {
+               TRACE("Adding id %d to id tables\n", id_table[i]);
+               create_id(id_table[i]);
+       }
+
+       return id_table;
+}
+
+
+struct squashfs_fragment_entry *read_fragment_table(int fd, struct squashfs_super_block *sBlk)
+{
+       int res, i;
+       int bytes = SQUASHFS_FRAGMENT_BYTES(sBlk->fragments);
+       int indexes = SQUASHFS_FRAGMENT_INDEXES(sBlk->fragments);
+       long long fragment_table_index[indexes];
+       struct squashfs_fragment_entry *fragment_table;
+
+       TRACE("read_fragment_table: %d fragments, reading %d fragment indexes "
+               "from 0x%llx\n", sBlk->fragments, indexes,
+               sBlk->fragment_table_start);
+
+       fragment_table = malloc(bytes);
+       if(fragment_table == NULL)
+               MEM_ERROR();
+
+       res = read_fs_bytes(fd, sBlk->fragment_table_start,
+               SQUASHFS_FRAGMENT_INDEX_BYTES(sBlk->fragments),
+               fragment_table_index);
+       if(res == 0) {
+               ERROR("Failed to read fragment table index\n");
+               ERROR("Filesystem corrupted?\n");
+               free(fragment_table);
+               return NULL;
+       }
+
+       SQUASHFS_INSWAP_FRAGMENT_INDEXES(fragment_table_index, indexes);
+
+       for(i = 0; i < indexes; i++) {
+               int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
+                                       bytes & (SQUASHFS_METADATA_SIZE - 1);
+               int length = read_block(fd, fragment_table_index[i], NULL,
+                       expected, ((unsigned char *) fragment_table) +
+                       (i * SQUASHFS_METADATA_SIZE));
+               TRACE("Read fragment table block %d, from 0x%llx, length %d\n",
+                       i, fragment_table_index[i], length);
+               if(length == 0) {
+                       ERROR("Failed to read fragment table block %d, from "
+                               "0x%llx, length %d\n", i,
+                               fragment_table_index[i], length);
+                       ERROR("Filesystem corrupted?\n");
+                       free(fragment_table);
+                       return NULL;
+               }
+       }
+
+       for(i = 0; i < sBlk->fragments; i++)
+               SQUASHFS_INSWAP_FRAGMENT_ENTRY(&fragment_table[i]);
+
+       return fragment_table;
+}
+
+
+squashfs_inode *read_inode_lookup_table(int fd, struct squashfs_super_block *sBlk)
+{
+       int lookup_bytes = SQUASHFS_LOOKUP_BYTES(sBlk->inodes);
+       int indexes = SQUASHFS_LOOKUP_BLOCKS(sBlk->inodes);
+       long long index[indexes];
+       int res, i;
+       squashfs_inode *inode_lookup_table;
+
+       inode_lookup_table = malloc(lookup_bytes);
+       if(inode_lookup_table == NULL)
+               MEM_ERROR();
+
+       res = read_fs_bytes(fd, sBlk->lookup_table_start,
+               SQUASHFS_LOOKUP_BLOCK_BYTES(sBlk->inodes), index);
+       if(res == 0) {
+               ERROR("Failed to read inode lookup table index\n");
+               ERROR("Filesystem corrupted?\n");
+               free(inode_lookup_table);
+               return NULL;
+       }
+
+       SQUASHFS_INSWAP_LONG_LONGS(index, indexes);
+
+       for(i = 0; i <  indexes; i++) {
+               int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
+                               lookup_bytes & (SQUASHFS_METADATA_SIZE - 1);
+               int length = read_block(fd, index[i], NULL, expected,
+                       ((unsigned char *) inode_lookup_table) +
+                       (i * SQUASHFS_METADATA_SIZE));
+               TRACE("Read inode lookup table block %d, from 0x%llx, length "
+                       "%d\n", i, index[i], length);
+               if(length == 0) {
+                       ERROR("Failed to read inode lookup table block %d, "
+                               "from 0x%llx, length %d\n", i, index[i],
+                               length);
+                       ERROR("Filesystem corrupted?\n");
+                       free(inode_lookup_table);
+                       return NULL;
+               }
+       }
+
+       SQUASHFS_INSWAP_LONG_LONGS(inode_lookup_table, sBlk->inodes);
+
+       return inode_lookup_table;
+}
+
+
+long long read_filesystem(char *root_name, int fd, struct squashfs_super_block *sBlk,
+       char **cinode_table, char **data_cache, char **cdirectory_table,
+       char **directory_data_cache, unsigned int *last_directory_block,
+       unsigned int *inode_dir_offset, unsigned int *inode_dir_file_size,
+       unsigned int *root_inode_size, unsigned int *inode_dir_start_block,
+       int *file_count, int *sym_count, int *dev_count, int *dir_count,
+       int *fifo_count, int *sock_count, long long *uncompressed_file,
+       unsigned int *uncompressed_inode, unsigned int *uncompressed_directory,
+       unsigned int *inode_dir_inode_number,
+       unsigned int *inode_dir_parent_inode,
+       void (push_directory_entry)(char *, squashfs_inode, int, int),
+       struct squashfs_fragment_entry **fragment_table,
+       squashfs_inode **inode_lookup_table)
+{
+       unsigned char *inode_table = NULL, *directory_table = NULL;
+       long long start = sBlk->inode_table_start;
+       long long end = sBlk->directory_table_start;
+       long long root_inode_start = start +
+               SQUASHFS_INODE_BLK(sBlk->root_inode);
+       unsigned int root_inode_offset =
+               SQUASHFS_INODE_OFFSET(sBlk->root_inode);
+       unsigned int root_inode_block;
+       union squashfs_inode_header inode;
+       unsigned int *id_table = NULL;
+       int res;
+
+       printf("Scanning existing filesystem...\n");
+
+       if(get_xattrs(fd, sBlk) == 0)
+               goto error;
+
+       if(sBlk->fragments > 0) {
+               *fragment_table = read_fragment_table(fd, sBlk);
+               if(*fragment_table == NULL)
+                       goto error;
+       }
+
+       if(sBlk->lookup_table_start != SQUASHFS_INVALID_BLK) {
+               *inode_lookup_table = read_inode_lookup_table(fd, sBlk);
+               if(*inode_lookup_table == NULL)
+                       goto error;
+       }
+
+       id_table = read_id_table(fd, sBlk);
+       if(id_table == NULL)
+               goto error;
+
+       inode_table = scan_inode_table(fd, start, end, root_inode_start,
+               root_inode_offset, sBlk, &inode, &root_inode_block,
+               root_inode_size, uncompressed_file, uncompressed_directory,
+               file_count, sym_count, dev_count, dir_count, fifo_count,
+               sock_count, id_table);
+       if(inode_table == NULL)
+               goto error;
+
+       *uncompressed_inode = root_inode_block;
+
+       if(inode.base.inode_type == SQUASHFS_DIR_TYPE ||
+                       inode.base.inode_type == SQUASHFS_LDIR_TYPE) {
+               if(inode.base.inode_type == SQUASHFS_DIR_TYPE) {
+                       *inode_dir_start_block = inode.dir.start_block;
+                       *inode_dir_offset = inode.dir.offset;
+                       *inode_dir_file_size = inode.dir.file_size - 3;
+                       *inode_dir_inode_number = inode.dir.inode_number;
+                       *inode_dir_parent_inode = inode.dir.parent_inode;
+               } else {
+                       *inode_dir_start_block = inode.ldir.start_block;
+                       *inode_dir_offset = inode.ldir.offset;
+                       *inode_dir_file_size = inode.ldir.file_size - 3;
+                       *inode_dir_inode_number = inode.ldir.inode_number;
+                       *inode_dir_parent_inode = inode.ldir.parent_inode;
+               }
+
+               directory_table = squashfs_readdir(fd, !root_name,
+                       *inode_dir_start_block, *inode_dir_offset,
+                       *inode_dir_file_size, last_directory_block, sBlk,
+                       push_directory_entry);
+               if(directory_table == NULL) 
+                       goto error;
+
+               root_inode_start -= start;
+               *cinode_table = malloc(root_inode_start);
+               if(*cinode_table == NULL)
+                       MEM_ERROR();
+
+               res = read_fs_bytes(fd, start, root_inode_start, *cinode_table);
+               if(res == 0) {
+                       ERROR("Failed to read inode table\n");
+                       ERROR("Filesystem corrupted?\n");
+                       goto error;
+               }
+
+               *cdirectory_table = malloc(*last_directory_block);
+               if(*cdirectory_table == NULL)
+                       MEM_ERROR();
+
+               res = read_fs_bytes(fd, sBlk->directory_table_start,
+                       *last_directory_block, *cdirectory_table);
+               if(res == 0) {
+                       ERROR("Failed to read directory table\n");
+                       ERROR("Filesystem corrupted?\n");
+                       goto error;
+               }
+
+               *data_cache = malloc(root_inode_offset + *root_inode_size);
+               if(*data_cache == NULL)
+                       MEM_ERROR();
+
+               memcpy(*data_cache, inode_table + root_inode_block,
+                       root_inode_offset + *root_inode_size);
+
+               *directory_data_cache = malloc(*inode_dir_offset +
+                       *inode_dir_file_size);
+               if(*directory_data_cache == NULL)
+                       MEM_ERROR();
+
+               memcpy(*directory_data_cache, directory_table,
+                       *inode_dir_offset + *inode_dir_file_size);
+
+               free(id_table);
+               free(inode_table);
+               free(directory_table);
+               return sBlk->inode_table_start;
+       }
+
+error:
+       free(id_table);
+       free(inode_table);
+       free(directory_table);
+       return 0;
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/read_fs.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/read_fs.h
new file mode 100644 (file)
index 0000000..9ad32c0
--- /dev/null
@@ -0,0 +1,34 @@
+#ifndef READ_FS_H
+#define READ_FS_H
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * read_fs.h
+ *
+ */
+extern struct compressor *read_super(int, struct squashfs_super_block *,
+       char *);
+extern long long read_filesystem(char *, int, struct squashfs_super_block *,
+char **, char **, char **, char **, unsigned int *, unsigned int *,
+unsigned int *, unsigned int *, unsigned int *, int *, int *, int *, int *,
+int *, int *, long long *, unsigned int *, unsigned int *, unsigned int *,
+unsigned int *, void (push_directory_entry)(char *, squashfs_inode, int, int),
+struct squashfs_fragment_entry **, squashfs_inode **);
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/read_xattrs.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/read_xattrs.c
new file mode 100644 (file)
index 0000000..4debedf
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+ * Read a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2010, 2012, 2013, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * read_xattrs.c
+ */
+
+/*
+ * Common xattr read code shared between mksquashfs and unsquashfs
+ */
+
+#define TRUE 1
+#define FALSE 0
+#include <stdio.h>
+#include <string.h>
+
+#ifndef linux
+#define __BYTE_ORDER BYTE_ORDER
+#define __BIG_ENDIAN BIG_ENDIAN
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#else
+#include <endian.h>
+#endif
+
+#include "squashfs_fs.h"
+#include "squashfs_swap.h"
+#include "xattr.h"
+#include "error.h"
+
+#include <stdlib.h>
+
+extern int read_fs_bytes(int, long long, int, void *);
+extern int read_block(int, long long, long long *, int, void *);
+
+static struct hash_entry {
+       long long               start;
+       unsigned int            offset;
+       struct hash_entry       *next;
+} *hash_table[65536];
+
+static struct squashfs_xattr_id *xattr_ids;
+static void *xattrs = NULL;
+static long long xattr_table_start;
+
+/*
+ * Prefix lookup table, storing mapping to/from prefix string and prefix id
+ */
+struct prefix prefix_table[] = {
+       { "user.", SQUASHFS_XATTR_USER },
+       { "trusted.", SQUASHFS_XATTR_TRUSTED },
+       { "security.", SQUASHFS_XATTR_SECURITY },
+       { "", -1 }
+};
+
+/*
+ * store mapping from location of compressed block in fs ->
+ * location of uncompressed block in memory
+ */
+static void save_xattr_block(long long start, int offset)
+{
+       struct hash_entry *hash_entry = malloc(sizeof(*hash_entry));
+       int hash = start & 0xffff;
+
+       TRACE("save_xattr_block: start %lld, offset %d\n", start, offset);
+
+       if(hash_entry == NULL)
+               MEM_ERROR();
+
+       hash_entry->start = start;
+       hash_entry->offset = offset;
+       hash_entry->next = hash_table[hash];
+       hash_table[hash] = hash_entry;
+}
+
+
+/*
+ * map from location of compressed block in fs ->
+ * location of uncompressed block in memory
+ */
+static int get_xattr_block(long long start)
+{
+       int hash = start & 0xffff;
+       struct hash_entry *hash_entry = hash_table[hash];
+
+       for(; hash_entry; hash_entry = hash_entry->next)
+               if(hash_entry->start == start)
+                       break;
+
+       TRACE("get_xattr_block: start %lld, offset %d\n", start,
+               hash_entry ? hash_entry->offset : -1);
+
+       return hash_entry ? hash_entry->offset : -1;
+}
+
+
+/*
+ * construct the xattr_list entry from the fs xattr, including
+ * mapping name and prefix into a full name
+ */
+static int read_xattr_entry(struct xattr_list *xattr,
+       struct squashfs_xattr_entry *entry, void *name)
+{
+       int i, len, type = entry->type & XATTR_PREFIX_MASK;
+
+       for(i = 0; prefix_table[i].type != -1; i++)
+               if(prefix_table[i].type == type)
+                       break;
+
+       if(prefix_table[i].type == -1) {
+               ERROR("read_xattr_entry: Unrecognised xattr type %d\n", type);
+               return 0;
+       }
+
+       len = strlen(prefix_table[i].prefix);
+       xattr->full_name = malloc(len + entry->size + 1);
+       if(xattr->full_name == NULL)
+               MEM_ERROR();
+
+       memcpy(xattr->full_name, prefix_table[i].prefix, len);
+       memcpy(xattr->full_name + len, name, entry->size);
+       xattr->full_name[len + entry->size] = '\0';
+       xattr->name = xattr->full_name + len;
+       xattr->size = entry->size;
+       xattr->type = type;
+
+       return 1;
+}
+
+
+/*
+ * Read and decompress the xattr id table and the xattr metadata.
+ * This is cached in memory for later use by get_xattr()
+ */
+int read_xattrs_from_disk(int fd, struct squashfs_super_block *sBlk, int flag, long long *table_start)
+{
+       /*
+        * Note on overflow limits:
+        * Size of ids (id_table.xattr_ids) is 2^32 (unsigned int)
+        * Max size of bytes is 2^32*16 or 2^36
+        * Max indexes is (2^32*16)/8K or 2^23
+        * Max index_bytes is ((2^32*16)/8K)*8 or 2^26 or 64M
+        */
+       int res, i, indexes, index_bytes;
+       unsigned int ids;
+       long long bytes;
+       long long *index, start, end;
+       struct squashfs_xattr_table id_table;
+
+       TRACE("read_xattrs_from_disk\n");
+
+       if(sBlk->xattr_id_table_start == SQUASHFS_INVALID_BLK)
+               return SQUASHFS_INVALID_BLK;
+
+       /*
+        * Read xattr id table, containing start of xattr metadata and the
+        * number of xattrs in the file system
+        */
+       res = read_fs_bytes(fd, sBlk->xattr_id_table_start, sizeof(id_table),
+               &id_table);
+       if(res == 0)
+               return 0;
+
+       SQUASHFS_INSWAP_XATTR_TABLE(&id_table);
+
+       /*
+        * Compute index table values
+        */
+       ids = id_table.xattr_ids;
+       xattr_table_start = id_table.xattr_table_start;
+       index_bytes = SQUASHFS_XATTR_BLOCK_BYTES((long long) ids);
+       indexes = SQUASHFS_XATTR_BLOCKS((long long) ids);
+
+       /*
+        * The size of the index table (index_bytes) should match the
+        * table start and end points
+        */
+       if(index_bytes != (sBlk->bytes_used - (sBlk->xattr_id_table_start + sizeof(id_table)))) {
+               ERROR("read_xattrs_from_disk: Bad xattr_ids count in super block\n");
+               return 0;
+       }
+
+       /*
+        * id_table.xattr_table_start stores the start of the compressed xattr
+        * metadata blocks.  This by definition is also the end of the previous
+        * filesystem table - the id lookup table.
+        */
+       if(table_start != NULL)
+               *table_start = id_table.xattr_table_start;
+
+       /*
+        * If flag is set then return once we've read the above
+        * table_start.  That value is necessary for sanity checking,
+        * but we don't actually want to extract the xattrs, and so
+        * stop here.
+        */
+       if(flag)
+               return id_table.xattr_ids;
+
+       /*
+        * Allocate and read the index to the xattr id table metadata
+        * blocks
+        */
+       index = malloc(index_bytes);
+       if(index == NULL)
+               MEM_ERROR();
+
+       res = read_fs_bytes(fd, sBlk->xattr_id_table_start + sizeof(id_table),
+               index_bytes, index);
+       if(res ==0)
+               goto failed1;
+
+       SQUASHFS_INSWAP_LONG_LONGS(index, indexes);
+
+       /*
+        * Allocate enough space for the uncompressed xattr id table, and
+        * read and decompress it
+        */
+       bytes = SQUASHFS_XATTR_BYTES((long long) ids);
+       xattr_ids = malloc(bytes);
+       if(xattr_ids == NULL)
+               MEM_ERROR();
+
+       for(i = 0; i < indexes; i++) {
+               int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
+                                       bytes & (SQUASHFS_METADATA_SIZE - 1);
+               int length = read_block(fd, index[i], NULL, expected,
+                       ((unsigned char *) xattr_ids) +
+                       ((long long) i * SQUASHFS_METADATA_SIZE));
+               TRACE("Read xattr id table block %d, from 0x%llx, length "
+                       "%d\n", i, index[i], length);
+               if(length == 0) {
+                       ERROR("Failed to read xattr id table block %d, "
+                               "from 0x%llx, length %d\n", i, index[i],
+                               length);
+                       goto failed2;
+               }
+       }
+
+       /*
+        * Read and decompress the xattr metadata
+        *
+        * Note the first xattr id table metadata block is immediately after
+        * the last xattr metadata block, so we can use index[0] to work out
+        * the end of the xattr metadata
+        */
+       start = xattr_table_start;
+       end = index[0];
+       for(i = 0; start < end; i++) {
+               int length;
+               xattrs = realloc(xattrs, (i + 1) * SQUASHFS_METADATA_SIZE);
+               if(xattrs == NULL)
+                       MEM_ERROR();
+
+               /* store mapping from location of compressed block in fs ->
+                * location of uncompressed block in memory */
+               save_xattr_block(start, i * SQUASHFS_METADATA_SIZE);
+
+               length = read_block(fd, start, &start, 0,
+                       ((unsigned char *) xattrs) +
+                       (i * SQUASHFS_METADATA_SIZE));
+               TRACE("Read xattr block %d, length %d\n", i, length);
+               if(length == 0) {
+                       ERROR("Failed to read xattr block %d\n", i);
+                       goto failed3;
+               }
+
+               /*
+                * If this is not the last metadata block in the xattr metadata
+                * then it should be SQUASHFS_METADATA_SIZE in size.
+                * Note, we can't use expected in read_block() above for this
+                * because we don't know if this is the last block until
+                * after reading.
+                */
+               if(start != end && length != SQUASHFS_METADATA_SIZE) {
+                       ERROR("Xattr block %d should be %d bytes in length, "
+                               "it is %d bytes\n", i, SQUASHFS_METADATA_SIZE,
+                               length);
+                       goto failed3;
+               }
+       }
+
+       /* swap if necessary the xattr id entries */
+       for(i = 0; i < ids; i++)
+               SQUASHFS_INSWAP_XATTR_ID(&xattr_ids[i]);
+
+       free(index);
+
+       return ids;
+
+failed3:
+       free(xattrs);
+failed2:
+       free(xattr_ids);
+failed1:
+       free(index);
+
+       return 0;
+}
+
+
+void free_xattr(struct xattr_list *xattr_list, int count)
+{
+       int i;
+
+       for(i = 0; i < count; i++)
+               free(xattr_list[i].full_name);
+
+       free(xattr_list);
+}
+
+
+/*
+ * Construct and return the list of xattr name:value pairs for the passed xattr
+ * id
+ *
+ * There are two users for get_xattr(), Mksquashfs uses it to read the
+ * xattrs from the filesystem on appending, and Unsquashfs uses it
+ * to retrieve the xattrs for writing to disk.
+ *
+ * Unfortunately, the two users disagree on what to do with unknown
+ * xattr prefixes, Mksquashfs wants to treat this as fatal otherwise
+ * this will cause xattrs to be be lost on appending.  Unsquashfs
+ * on the otherhand wants to retrieve the xattrs which are known and
+ * to ignore the rest, this allows Unsquashfs to cope more gracefully
+ * with future versions which may have unknown xattrs, as long as the
+ * general xattr structure is adhered to, Unsquashfs should be able
+ * to safely ignore unknown xattrs, and to write the ones it knows about,
+ * this is better than completely refusing to retrieve all the xattrs.
+ *
+ * So return an error flag if any unrecognised types were found.
+ */
+struct xattr_list *get_xattr(int i, unsigned int *count, int *failed)
+{
+       long long start;
+       struct xattr_list *xattr_list = NULL;
+       unsigned int offset;
+       void *xptr;
+       int j, n, res = 1;
+
+       TRACE("get_xattr\n");
+
+       if(xattr_ids[i].count == 0) {
+               ERROR("get_xattr: xattr count unexpectedly 0 - corrupt fs?\n");
+               *failed = TRUE;
+               *count = 0;
+               return NULL;
+       } else
+               *failed = FALSE;
+
+       start = SQUASHFS_XATTR_BLK(xattr_ids[i].xattr) + xattr_table_start;
+       offset = SQUASHFS_XATTR_OFFSET(xattr_ids[i].xattr);
+       xptr = xattrs + get_xattr_block(start) + offset;
+
+       TRACE("get_xattr: xattr_id %d, count %d, start %lld, offset %d\n", i,
+                       xattr_ids[i].count, start, offset);
+
+       for(j = 0, n = 0; n < xattr_ids[i].count; n++) {
+               struct squashfs_xattr_entry entry;
+               struct squashfs_xattr_val val;
+
+               if(res != 0) {
+                       xattr_list = realloc(xattr_list, (j + 1) *
+                                               sizeof(struct xattr_list));
+                       if(xattr_list == NULL)
+                               MEM_ERROR();
+               }
+                       
+               SQUASHFS_SWAP_XATTR_ENTRY(xptr, &entry);
+               xptr += sizeof(entry);
+
+               res = read_xattr_entry(&xattr_list[j], &entry, xptr);
+               if(res == 0) {
+                       /* unknown type, skip, and set error flag */
+                       xptr += entry.size;
+                       SQUASHFS_SWAP_XATTR_VAL(xptr, &val);
+                       xptr += sizeof(val) + val.vsize;
+                       *failed = TRUE;
+                       continue;
+               }
+
+               xptr += entry.size;
+                       
+               TRACE("get_xattr: xattr %d, type %d, size %d, name %s\n", j,
+                       entry.type, entry.size, xattr_list[j].full_name); 
+
+               if(entry.type & SQUASHFS_XATTR_VALUE_OOL) {
+                       long long xattr;
+                       void *ool_xptr;
+
+                       xptr += sizeof(val);
+                       SQUASHFS_SWAP_LONG_LONGS(xptr, &xattr, 1);
+                       xptr += sizeof(xattr);  
+                       start = SQUASHFS_XATTR_BLK(xattr) + xattr_table_start;
+                       offset = SQUASHFS_XATTR_OFFSET(xattr);
+                       ool_xptr = xattrs + get_xattr_block(start) + offset;
+                       SQUASHFS_SWAP_XATTR_VAL(ool_xptr, &val);
+                       xattr_list[j].value = ool_xptr + sizeof(val);
+               } else {
+                       SQUASHFS_SWAP_XATTR_VAL(xptr, &val);
+                       xattr_list[j].value = xptr + sizeof(val);
+                       xptr += sizeof(val) + val.vsize;
+               }
+
+               TRACE("get_xattr: xattr %d, vsize %d\n", j, val.vsize);
+
+               xattr_list[j++].vsize = val.vsize;
+       }
+
+       *count = j;
+       return xattr_list;
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/restore.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/restore.c
new file mode 100644 (file)
index 0000000..aedeca2
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2013, 2014, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * restore.c
+ */
+
+#include <pthread.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "caches-queues-lists.h"
+#include "squashfs_fs.h"
+#include "mksquashfs.h"
+#include "error.h"
+#include "progressbar.h"
+#include "info.h"
+
+#define FALSE 0
+#define TRUE 1
+
+extern pthread_t reader_thread, writer_thread, main_thread, order_thread;
+extern pthread_t *deflator_thread, *frag_deflator_thread, *frag_thread;
+extern struct queue *to_deflate, *to_writer, *to_frag, *to_process_frag;
+extern struct seq_queue *to_main, *to_order;
+extern void restorefs();
+extern int processors;
+extern int reproducible;
+
+static int interrupted = 0;
+static pthread_t restore_thread;
+
+void *restore_thrd(void *arg)
+{
+       sigset_t sigmask, old_mask;
+       int i, sig;
+
+       sigemptyset(&sigmask);
+       sigaddset(&sigmask, SIGINT);
+       sigaddset(&sigmask, SIGTERM);
+       sigaddset(&sigmask, SIGUSR1);
+       pthread_sigmask(SIG_BLOCK, &sigmask, &old_mask);
+
+       while(1) {
+               sigwait(&sigmask, &sig);
+
+               if((sig == SIGINT || sig == SIGTERM) && !interrupted) {
+                       ERROR("Interrupting will restore original "
+                               "filesystem!\n");
+                       ERROR("Interrupt again to quit\n");
+                       interrupted = TRUE;
+                       continue;
+               }
+
+               /* kill main thread/worker threads and restore */
+               set_progressbar_state(FALSE);
+               disable_info();
+
+               /* first kill the reader thread */
+               pthread_cancel(reader_thread);
+               pthread_join(reader_thread, NULL);
+
+               /*
+                * then flush the reader to deflator thread(s) output queue.
+                * The deflator thread(s) will idle
+                */
+               queue_flush(to_deflate);
+
+               /* now kill the deflator thread(s) */
+               for(i = 0; i < processors; i++)
+                       pthread_cancel(deflator_thread[i]);
+               for(i = 0; i < processors; i++)
+                       pthread_join(deflator_thread[i], NULL);
+
+               /*
+                * then flush the reader to process fragment thread(s) output
+                * queue.  The process fragment thread(s) will idle
+                */
+               queue_flush(to_process_frag);
+
+               /* now kill the process fragment thread(s) */
+               for(i = 0; i < processors; i++)
+                       pthread_cancel(frag_thread[i]);
+               for(i = 0; i < processors; i++)
+                       pthread_join(frag_thread[i], NULL);
+
+               /*
+                * then flush the reader/deflator/process fragment to main
+                * thread output queue.  The main thread will idle
+                */
+               seq_queue_flush(to_main);
+
+               /* now kill the main thread */
+               pthread_cancel(main_thread);
+               pthread_join(main_thread, NULL);
+
+               /* then flush the main thread to fragment deflator thread(s)
+                * queue.  The fragment deflator thread(s) will idle
+                */
+               queue_flush(to_frag);
+
+               /* now kill the fragment deflator thread(s) */
+               for(i = 0; i < processors; i++)
+                       pthread_cancel(frag_deflator_thread[i]);
+               for(i = 0; i < processors; i++)
+                       pthread_join(frag_deflator_thread[i], NULL);
+
+               if(reproducible) {
+                       /* then flush the fragment deflator_threads(s)
+                        * to frag orderer thread.  The frag orderer
+                        * thread will idle
+                        */
+                       seq_queue_flush(to_order);
+
+                       /* now kill the frag orderer thread */
+                       pthread_cancel(order_thread);
+                       pthread_join(order_thread, NULL);
+               }
+
+               /*
+                * then flush the main thread/fragment deflator thread(s)
+                * to writer thread queue.  The writer thread will idle
+                */
+               queue_flush(to_writer);
+
+               /* now kill the writer thread */
+               pthread_cancel(writer_thread);
+               pthread_join(writer_thread, NULL);
+
+               TRACE("All threads cancelled\n");
+
+               restorefs();
+       }
+}
+
+
+pthread_t *init_restore_thread()
+{
+       pthread_create(&restore_thread, NULL, restore_thrd, NULL);
+       return &restore_thread;
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/restore.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/restore.h
new file mode 100644 (file)
index 0000000..35129f0
--- /dev/null
@@ -0,0 +1,28 @@
+#ifndef RESTORE_H
+#define RESTORE_H
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2013, 2014
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * restore.h
+ */
+
+extern pthread_t *init_restore_thread();
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/sort.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/sort.c
new file mode 100644 (file)
index 0000000..89df9e4
--- /dev/null
@@ -0,0 +1,363 @@
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012,
+ * 2013, 2014
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * sort.c
+ */
+
+#define TRUE 1
+#define FALSE 0
+#define MAX_LINE 16384
+
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
+
+#include "squashfs_fs.h"
+#include "mksquashfs.h"
+#include "sort.h"
+#include "error.h"
+#include "progressbar.h"
+
+int mkisofs_style = -1;
+
+struct sort_info {
+       dev_t                   st_dev;
+       ino_t                   st_ino;
+       int                     priority;
+       struct sort_info        *next;
+};
+
+struct sort_info *sort_info_list[65536];
+
+struct priority_entry *priority_list[65536];
+
+extern int silent;
+extern void write_file(squashfs_inode *inode, struct dir_ent *dir_ent,
+       int *c_size);
+extern char *pathname(struct dir_ent *dir_ent);
+
+
+void add_priority_list(struct dir_ent *dir, int priority)
+{
+       struct priority_entry *new_priority_entry;
+
+       priority += 32768;
+       new_priority_entry = malloc(sizeof(struct priority_entry));
+       if(new_priority_entry == NULL)
+               MEM_ERROR();
+
+       new_priority_entry->dir = dir;;
+       new_priority_entry->next = priority_list[priority];
+       priority_list[priority] = new_priority_entry;
+}
+
+
+int get_priority(char *filename, struct stat *buf, int priority)
+{
+       int hash = buf->st_ino & 0xffff;
+       struct sort_info *s;
+
+       for(s = sort_info_list[hash]; s; s = s->next)
+               if((s->st_dev == buf->st_dev) && (s->st_ino == buf->st_ino)) {
+                       TRACE("returning priority %d (%s)\n", s->priority,
+                               filename);
+                       return s->priority;
+               }
+       TRACE("returning priority %d (%s)\n", priority, filename);
+       return priority;
+}
+
+
+#define ADD_ENTRY(buf, priority) {\
+       int hash = buf.st_ino & 0xffff;\
+       struct sort_info *s;\
+       if((s = malloc(sizeof(struct sort_info))) == NULL) \
+               MEM_ERROR(); \
+       s->st_dev = buf.st_dev;\
+       s->st_ino = buf.st_ino;\
+       s->priority = priority;\
+       s->next = sort_info_list[hash];\
+       sort_info_list[hash] = s;\
+       }
+int add_sort_list(char *path, int priority, int source, char *source_path[])
+{
+       int i, n;
+       struct stat buf;
+
+       TRACE("add_sort_list: filename %s, priority %d\n", path, priority);
+       if(strlen(path) > 1 && strcmp(path + strlen(path) - 2, "/*") == 0)
+               path[strlen(path) - 2] = '\0';
+
+       TRACE("add_sort_list: filename %s, priority %d\n", path, priority);
+re_read:
+       if(path[0] == '/' || strncmp(path, "./", 2) == 0 ||
+                       strncmp(path, "../", 3) == 0 || mkisofs_style == 1) {
+               if(lstat(path, &buf) == -1)
+                       goto error;
+               TRACE("adding filename %s, priority %d, st_dev %d, st_ino "
+                       "%lld\n", path, priority, (int) buf.st_dev,
+                       (long long) buf.st_ino);
+               ADD_ENTRY(buf, priority);
+               return TRUE;
+       }
+
+       for(i = 0, n = 0; i < source; i++) {
+               char *filename;
+               int res = asprintf(&filename, "%s/%s", source_path[i], path);
+               if(res == -1)
+                       BAD_ERROR("asprintf failed in add_sort_list\n");
+               res = lstat(filename, &buf);
+               free(filename);
+               if(res == -1) {
+                       if(!(errno == ENOENT || errno == ENOTDIR))
+                               goto error;
+                       continue;
+               }
+               ADD_ENTRY(buf, priority);
+               n ++;
+       }
+
+       if(n == 0 && mkisofs_style == -1 && lstat(path, &buf) != -1) {
+               ERROR("WARNING: Mkisofs style sortlist detected! This is "
+                       "supported but please\n");
+               ERROR("convert to mksquashfs style sortlist! A sortlist entry");
+               ERROR(" should be\neither absolute (starting with ");
+               ERROR("'/') start with './' or '../' (taken to be\nrelative to "
+                       "$PWD), otherwise it ");
+               ERROR("is assumed the entry is relative to one\nof the source "
+                       "directories, i.e. with ");
+               ERROR("\"mksquashfs test test.sqsh\",\nthe sortlist ");
+               ERROR("entry \"file\" is assumed to be inside the directory "
+                       "test.\n\n");
+               mkisofs_style = 1;
+               goto re_read;
+       }
+
+       mkisofs_style = 0;
+
+       if(n == 1)
+               return TRUE;
+       if(n > 1) {
+               ERROR(" Ambiguous sortlist entry \"%s\"\n\nIt maps to more "
+                       "than one source entry!  Please use an absolute path."
+                       "\n", path);
+               return FALSE;
+       }
+
+error:
+        ERROR_START("Cannot stat sortlist entry \"%s\"\n", path);
+        ERROR("This is probably because you're using the wrong file\n");
+        ERROR("path relative to the source directories.");
+       ERROR_EXIT("  Ignoring");
+       /*
+        * Historical note
+        * Failure to stat a sortlist entry is deliberately ignored, even
+        * though it is an error.  Squashfs release 2.2 changed the behaviour
+        * to treat it as a fatal error, but it was changed back to
+        * the original behaviour to ignore it in release 2.2-r2 following
+        * feedback from users at the time.
+        */
+        return TRUE;
+}
+
+
+void generate_file_priorities(struct dir_info *dir, int priority,
+       struct stat *buf)
+{
+       struct dir_ent *dir_ent = dir->list;
+
+       priority = get_priority(dir->pathname, buf, priority);
+
+       for(; dir_ent; dir_ent = dir_ent->next) {
+               struct stat *buf = &dir_ent->inode->buf;
+               if(dir_ent->inode->root_entry)
+                       continue;
+
+               switch(buf->st_mode & S_IFMT) {
+                       case S_IFREG:
+                               add_priority_list(dir_ent,
+                                       get_priority(pathname(dir_ent), buf,
+                                       priority));
+                               break;
+                       case S_IFDIR:
+                               generate_file_priorities(dir_ent->dir,
+                                       priority, buf);
+                               break;
+               }
+       }
+}
+
+
+int read_sort_file(char *filename, int source, char *source_path[])
+{
+       FILE *fd;
+       char line_buffer[MAX_LINE + 1]; /* overflow safe */
+       char sort_filename[MAX_LINE + 1]; /* overflow safe */
+       char *line, *name;
+       int n, priority, res;
+
+       if((fd = fopen(filename, "r")) == NULL) {
+               ERROR("Failed to open sort file \"%s\" because %s\n",
+                       filename, strerror(errno));
+               return FALSE;
+       }
+
+       while(fgets(line = line_buffer, MAX_LINE + 1, fd) != NULL) {
+               int len = strlen(line);
+
+               if(len == MAX_LINE && line[len - 1] != '\n') {
+                       /* line too large */
+                       ERROR("Line too long when reading "
+                               "sort file \"%s\", larger than %d "
+                               "bytes\n", filename, MAX_LINE);
+                       goto failed;
+               }
+
+               /*
+                * Remove '\n' terminator if it exists (the last line
+                * in the file may not be '\n' terminated)
+                */
+               if(len && line[len - 1] == '\n')
+                       line[len - 1] = '\0';
+
+               /* Skip any leading whitespace */
+               while(isspace(*line))
+                       line ++;
+
+               /* if comment line, skip */
+               if(*line == '#')
+                       continue;
+
+               /*
+                * Scan for filename, don't use sscanf() and "%s" because
+                * that can't handle filenames with spaces
+                */
+               for(name = sort_filename; !isspace(*line) && *line != '\0';) {
+                       if(*line == '\\') {
+                               line ++;
+                               if (*line == '\0')
+                                       break;
+                       }
+                       *name ++ = *line ++;
+               }
+               *name = '\0';
+
+               /*
+                * if filename empty, then line was empty of anything but
+                * whitespace or a backslash character.  Skip empy lines
+                */
+               if(sort_filename[0] == '\0')
+                       continue;
+
+               /*
+                * Scan the rest of the line, we expect a decimal number
+                * which is the filename priority
+                */
+               errno = 0;
+               res = sscanf(line, "%d%n", &priority, &n);
+
+               if((res < 1 || errno) && errno != ERANGE) {
+                       if(errno == 0)
+                               /* No error, assume EOL or match failure */
+                               ERROR("Sort file \"%s\", can't find priority "
+                                       "in entry \"%s\", EOL or match "
+                                       "failure\n", filename, line_buffer);
+                       else
+                               /* Some other failure not ERANGE */
+                               ERROR("Sscanf failed reading sort file \"%s\" "
+                                       "because %s\n", filename,
+                                       strerror(errno));
+                       goto failed;
+               } else if((errno == ERANGE) ||
+                               (priority < -32768 || priority > 32767)) {
+                       ERROR("Sort file \"%s\", entry \"%s\" has priority "
+                               "outside range of -32767:32768.\n", filename,
+                               line_buffer);
+                       goto failed;
+               }
+
+               /* Skip any trailing whitespace */
+               line += n;
+               while(isspace(*line))
+                       line ++;
+
+               if(*line != '\0') {
+                       ERROR("Sort file \"%s\", trailing characters after "
+                               "priority in entry \"%s\"\n", filename,
+                               line_buffer);
+                       goto failed;
+               }
+
+               res = add_sort_list(sort_filename, priority, source,
+                       source_path);
+               if(res == FALSE)
+                       goto failed;
+       }
+
+       if(ferror(fd)) {
+               ERROR("Reading sort file \"%s\" failed because %s\n", filename,
+                       strerror(errno));
+               goto failed;
+       }
+
+       fclose(fd);
+       return TRUE;
+
+failed:
+       fclose(fd);
+       return FALSE;
+}
+
+
+void sort_files_and_write(struct dir_info *dir)
+{
+       int i;
+       struct priority_entry *entry;
+       squashfs_inode inode;
+       int duplicate_file;
+
+       for(i = 65535; i >= 0; i--)
+               for(entry = priority_list[i]; entry; entry = entry->next) {
+                       TRACE("%d: %s\n", i - 32768, pathname(entry->dir));
+                       if(entry->dir->inode->inode == SQUASHFS_INVALID_BLK) {
+                               write_file(&inode, entry->dir, &duplicate_file);
+                               INFO("file %s, uncompressed size %lld bytes %s"
+                                       "\n", pathname(entry->dir),
+                                       (long long)
+                                       entry->dir->inode->buf.st_size,
+                                       duplicate_file ? "DUPLICATE" : "");
+                               entry->dir->inode->inode = inode;
+                               entry->dir->inode->type = SQUASHFS_FILE_TYPE;
+                       } else
+                               INFO("file %s, uncompressed size %lld bytes "
+                                       "LINK\n", pathname(entry->dir),
+                                       (long long)
+                                       entry->dir->inode->buf.st_size);
+               }
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/sort.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/sort.h
new file mode 100644 (file)
index 0000000..98db62c
--- /dev/null
@@ -0,0 +1,37 @@
+#ifndef SORT_H 
+#define SORT_H
+
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * sort.h
+ */
+
+struct priority_entry {
+       struct dir_ent *dir;
+       struct priority_entry *next;
+};
+
+extern int read_sort_file(char *, int, char *[]);
+extern void sort_files_and_write(struct dir_info *);
+extern void generate_file_priorities(struct dir_info *, int priority,
+       struct stat *);
+extern struct  priority_entry *priority_list[65536];
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/squashfs_compat.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/squashfs_compat.h
new file mode 100644 (file)
index 0000000..9359fee
--- /dev/null
@@ -0,0 +1,834 @@
+#ifndef SQUASHFS_COMPAT
+#define SQUASHFS_COMPAT
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2014, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * squashfs_compat.h
+ */
+
+/*
+ * definitions for structures on disk - layout 3.x
+ */
+
+#define SQUASHFS_CHECK                 2
+#define SQUASHFS_CHECK_DATA(flags)             SQUASHFS_BIT(flags, \
+                                               SQUASHFS_CHECK)
+
+/* Max number of uids and gids */
+#define SQUASHFS_UIDS                  256
+#define SQUASHFS_GUIDS                 255
+
+struct squashfs_super_block_3 {
+       unsigned int            s_magic;
+       unsigned int            inodes;
+       unsigned int            bytes_used_2;
+       unsigned int            uid_start_2;
+       unsigned int            guid_start_2;
+       unsigned int            inode_table_start_2;
+       unsigned int            directory_table_start_2;
+       unsigned int            s_major:16;
+       unsigned int            s_minor:16;
+       unsigned int            block_size_1:16;
+       unsigned int            block_log:16;
+       unsigned int            flags:8;
+       unsigned int            no_uids:8;
+       unsigned int            no_guids:8;
+       int                     mkfs_time /* time of filesystem creation */;
+       squashfs_inode          root_inode;
+       unsigned int            block_size;
+       unsigned int            fragments;
+       unsigned int            fragment_table_start_2;
+       long long               bytes_used;
+       long long               uid_start;
+       long long               guid_start;
+       long long               inode_table_start;
+       long long               directory_table_start;
+       long long               fragment_table_start;
+       long long               lookup_table_start;
+} __attribute__ ((packed));
+
+struct squashfs_dir_index_3 {
+       unsigned int            index;
+       unsigned int            start_block;
+       unsigned char           size;
+       unsigned char           name[0];
+} __attribute__ ((packed));
+
+struct squashfs_base_inode_header_3 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12;
+       unsigned int            uid:8;
+       unsigned int            guid:8;
+       int                     mtime;
+       unsigned int            inode_number;
+} __attribute__ ((packed));
+
+struct squashfs_ipc_inode_header_3 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12;
+       unsigned int            uid:8;
+       unsigned int            guid:8;
+       int                     mtime;
+       unsigned int            inode_number;
+       unsigned int            nlink;
+} __attribute__ ((packed));
+
+struct squashfs_dev_inode_header_3 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12;
+       unsigned int            uid:8;
+       unsigned int            guid:8;
+       int                     mtime;
+       unsigned int            inode_number;
+       unsigned int            nlink;
+       unsigned short          rdev;
+} __attribute__ ((packed));
+       
+struct squashfs_symlink_inode_header_3 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12;
+       unsigned int            uid:8;
+       unsigned int            guid:8;
+       int                     mtime;
+       unsigned int            inode_number;
+       unsigned int            nlink;
+       unsigned short          symlink_size;
+       char                    symlink[0];
+} __attribute__ ((packed));
+
+struct squashfs_reg_inode_header_3 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12;
+       unsigned int            uid:8;
+       unsigned int            guid:8;
+       int                     mtime;
+       unsigned int            inode_number;
+       squashfs_block          start_block;
+       unsigned int            fragment;
+       unsigned int            offset;
+       unsigned int            file_size;
+       unsigned short          block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_lreg_inode_header_3 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12;
+       unsigned int            uid:8;
+       unsigned int            guid:8;
+       int                     mtime;
+       unsigned int            inode_number;
+       unsigned int            nlink;
+       squashfs_block          start_block;
+       unsigned int            fragment;
+       unsigned int            offset;
+       long long               file_size;
+       unsigned short          block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_inode_header_3 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12;
+       unsigned int            uid:8;
+       unsigned int            guid:8;
+       int                     mtime;
+       unsigned int            inode_number;
+       unsigned int            nlink;
+       unsigned int            file_size:19;
+       unsigned int            offset:13;
+       unsigned int            start_block;
+       unsigned int            parent_inode;
+} __attribute__  ((packed));
+
+struct squashfs_ldir_inode_header_3 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12;
+       unsigned int            uid:8;
+       unsigned int            guid:8;
+       int                     mtime;
+       unsigned int            inode_number;
+       unsigned int            nlink;
+       unsigned int            file_size:27;
+       unsigned int            offset:13;
+       unsigned int            start_block;
+       unsigned int            i_count:16;
+       unsigned int            parent_inode;
+       struct squashfs_dir_index_3     index[0];
+} __attribute__  ((packed));
+
+union squashfs_inode_header_3 {
+       struct squashfs_base_inode_header_3     base;
+       struct squashfs_dev_inode_header_3      dev;
+       struct squashfs_symlink_inode_header_3  symlink;
+       struct squashfs_reg_inode_header_3      reg;
+       struct squashfs_lreg_inode_header_3     lreg;
+       struct squashfs_dir_inode_header_3      dir;
+       struct squashfs_ldir_inode_header_3     ldir;
+       struct squashfs_ipc_inode_header_3      ipc;
+};
+       
+struct squashfs_dir_entry_3 {
+       unsigned int            offset:13;
+       unsigned int            type:3;
+       unsigned int            size:8;
+       int                     inode_number:16;
+       char                    name[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_header_3 {
+       unsigned int            count:8;
+       unsigned int            start_block;
+       unsigned int            inode_number;
+} __attribute__ ((packed));
+
+struct squashfs_fragment_entry_3 {
+       long long               start_block;
+       unsigned int            size;
+       unsigned int            pending;
+} __attribute__ ((packed));
+
+
+typedef struct squashfs_super_block_3 squashfs_super_block_3;
+typedef struct squashfs_dir_index_3 squashfs_dir_index_3;
+typedef struct squashfs_base_inode_header_3 squashfs_base_inode_header_3;
+typedef struct squashfs_ipc_inode_header_3 squashfs_ipc_inode_header_3;
+typedef struct squashfs_dev_inode_header_3 squashfs_dev_inode_header_3;
+typedef struct squashfs_symlink_inode_header_3 squashfs_symlink_inode_header_3;
+typedef struct squashfs_reg_inode_header_3 squashfs_reg_inode_header_3;
+typedef struct squashfs_lreg_inode_header_3 squashfs_lreg_inode_header_3;
+typedef struct squashfs_dir_inode_header_3 squashfs_dir_inode_header_3;
+typedef struct squashfs_ldir_inode_header_3 squashfs_ldir_inode_header_3;
+typedef struct squashfs_dir_entry_3 squashfs_dir_entry_3;
+typedef struct squashfs_dir_header_3 squashfs_dir_header_3;
+typedef struct squashfs_fragment_entry_3 squashfs_fragment_entry_3;
+
+/*
+ * macros to convert each packed bitfield structure from little endian to big
+ * endian and vice versa.  These are needed when creating or using a filesystem
+ * on a machine with different byte ordering to the target architecture.
+ *
+ */
+
+#define SQUASHFS_SWAP_START \
+       int bits;\
+       int b_pos;\
+       unsigned long long val;\
+       unsigned char *s;\
+       unsigned char *d;
+
+#define SQUASHFS_SWAP_SUPER_BLOCK_3(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_super_block_3));\
+       SQUASHFS_SWAP((s)->s_magic, d, 0, 32);\
+       SQUASHFS_SWAP((s)->inodes, d, 32, 32);\
+       SQUASHFS_SWAP((s)->bytes_used_2, d, 64, 32);\
+       SQUASHFS_SWAP((s)->uid_start_2, d, 96, 32);\
+       SQUASHFS_SWAP((s)->guid_start_2, d, 128, 32);\
+       SQUASHFS_SWAP((s)->inode_table_start_2, d, 160, 32);\
+       SQUASHFS_SWAP((s)->directory_table_start_2, d, 192, 32);\
+       SQUASHFS_SWAP((s)->s_major, d, 224, 16);\
+       SQUASHFS_SWAP((s)->s_minor, d, 240, 16);\
+       SQUASHFS_SWAP((s)->block_size_1, d, 256, 16);\
+       SQUASHFS_SWAP((s)->block_log, d, 272, 16);\
+       SQUASHFS_SWAP((s)->flags, d, 288, 8);\
+       SQUASHFS_SWAP((s)->no_uids, d, 296, 8);\
+       SQUASHFS_SWAP((s)->no_guids, d, 304, 8);\
+       SQUASHFS_SWAP((s)->mkfs_time, d, 312, 32);\
+       SQUASHFS_SWAP((s)->root_inode, d, 344, 64);\
+       SQUASHFS_SWAP((s)->block_size, d, 408, 32);\
+       SQUASHFS_SWAP((s)->fragments, d, 440, 32);\
+       SQUASHFS_SWAP((s)->fragment_table_start_2, d, 472, 32);\
+       SQUASHFS_SWAP((s)->bytes_used, d, 504, 64);\
+       SQUASHFS_SWAP((s)->uid_start, d, 568, 64);\
+       SQUASHFS_SWAP((s)->guid_start, d, 632, 64);\
+       SQUASHFS_SWAP((s)->inode_table_start, d, 696, 64);\
+       SQUASHFS_SWAP((s)->directory_table_start, d, 760, 64);\
+       SQUASHFS_SWAP((s)->fragment_table_start, d, 824, 64);\
+       SQUASHFS_SWAP((s)->lookup_table_start, d, 888, 64);\
+}
+
+#define SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, n)\
+       SQUASHFS_MEMSET(s, d, n);\
+       SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
+       SQUASHFS_SWAP((s)->mode, d, 4, 12);\
+       SQUASHFS_SWAP((s)->uid, d, 16, 8);\
+       SQUASHFS_SWAP((s)->guid, d, 24, 8);\
+       SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
+       SQUASHFS_SWAP((s)->inode_number, d, 64, 32);
+
+#define SQUASHFS_SWAP_BASE_INODE_HEADER_3(s, d, n) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, n)\
+}
+
+#define SQUASHFS_SWAP_IPC_INODE_HEADER_3(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
+                       sizeof(struct squashfs_ipc_inode_header_3))\
+       SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+}
+
+#define SQUASHFS_SWAP_DEV_INODE_HEADER_3(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
+                       sizeof(struct squashfs_dev_inode_header_3)); \
+       SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+       SQUASHFS_SWAP((s)->rdev, d, 128, 16);\
+}
+
+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_3(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
+                       sizeof(struct squashfs_symlink_inode_header_3));\
+       SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+       SQUASHFS_SWAP((s)->symlink_size, d, 128, 16);\
+}
+
+#define SQUASHFS_SWAP_REG_INODE_HEADER_3(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
+                       sizeof(struct squashfs_reg_inode_header_3));\
+       SQUASHFS_SWAP((s)->start_block, d, 96, 64);\
+       SQUASHFS_SWAP((s)->fragment, d, 160, 32);\
+       SQUASHFS_SWAP((s)->offset, d, 192, 32);\
+       SQUASHFS_SWAP((s)->file_size, d, 224, 32);\
+}
+
+#define SQUASHFS_SWAP_LREG_INODE_HEADER_3(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
+                       sizeof(struct squashfs_lreg_inode_header_3));\
+       SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+       SQUASHFS_SWAP((s)->start_block, d, 128, 64);\
+       SQUASHFS_SWAP((s)->fragment, d, 192, 32);\
+       SQUASHFS_SWAP((s)->offset, d, 224, 32);\
+       SQUASHFS_SWAP((s)->file_size, d, 256, 64);\
+}
+
+#define SQUASHFS_SWAP_DIR_INODE_HEADER_3(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
+                       sizeof(struct squashfs_dir_inode_header_3));\
+       SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+       SQUASHFS_SWAP((s)->file_size, d, 128, 19);\
+       SQUASHFS_SWAP((s)->offset, d, 147, 13);\
+       SQUASHFS_SWAP((s)->start_block, d, 160, 32);\
+       SQUASHFS_SWAP((s)->parent_inode, d, 192, 32);\
+}
+
+#define SQUASHFS_SWAP_LDIR_INODE_HEADER_3(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_3(s, d, \
+                       sizeof(struct squashfs_ldir_inode_header_3));\
+       SQUASHFS_SWAP((s)->nlink, d, 96, 32);\
+       SQUASHFS_SWAP((s)->file_size, d, 128, 27);\
+       SQUASHFS_SWAP((s)->offset, d, 155, 13);\
+       SQUASHFS_SWAP((s)->start_block, d, 168, 32);\
+       SQUASHFS_SWAP((s)->i_count, d, 200, 16);\
+       SQUASHFS_SWAP((s)->parent_inode, d, 216, 32);\
+}
+
+#define SQUASHFS_SWAP_DIR_INDEX_3(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_3));\
+       SQUASHFS_SWAP((s)->index, d, 0, 32);\
+       SQUASHFS_SWAP((s)->start_block, d, 32, 32);\
+       SQUASHFS_SWAP((s)->size, d, 64, 8);\
+}
+
+#define SQUASHFS_SWAP_DIR_HEADER_3(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_3));\
+       SQUASHFS_SWAP((s)->count, d, 0, 8);\
+       SQUASHFS_SWAP((s)->start_block, d, 8, 32);\
+       SQUASHFS_SWAP((s)->inode_number, d, 40, 32);\
+}
+
+#define SQUASHFS_SWAP_DIR_ENTRY_3(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_3));\
+       SQUASHFS_SWAP((s)->offset, d, 0, 13);\
+       SQUASHFS_SWAP((s)->type, d, 13, 3);\
+       SQUASHFS_SWAP((s)->size, d, 16, 8);\
+       SQUASHFS_SWAP((s)->inode_number, d, 24, 16);\
+}
+
+#define SQUASHFS_SWAP_INODE_T_3(s, d) SQUASHFS_SWAP_LONG_LONGS_3(s, d, 1)
+
+#define SQUASHFS_SWAP_SHORTS_3(s, d, n) {\
+       int entry;\
+       int bit_position;\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, n * 2);\
+       for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+                       16)\
+               SQUASHFS_SWAP(s[entry], d, bit_position, 16);\
+}
+
+#define SQUASHFS_SWAP_INTS_3(s, d, n) {\
+       int entry;\
+       int bit_position;\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, n * 4);\
+       for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+                       32)\
+               SQUASHFS_SWAP(s[entry], d, bit_position, 32);\
+}
+
+#define SQUASHFS_SWAP_LONG_LONGS_3(s, d, n) {\
+       int entry;\
+       int bit_position;\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, n * 8);\
+       for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+                       64)\
+               SQUASHFS_SWAP(s[entry], d, bit_position, 64);\
+}
+
+#define SQUASHFS_SWAP_DATA(s, d, n, bits) {\
+       int entry;\
+       int bit_position;\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, n * bits / 8);\
+       for(entry = 0, bit_position = 0; entry < n; entry++, bit_position += \
+                       bits)\
+               SQUASHFS_SWAP(s[entry], d, bit_position, bits);\
+}
+
+#define SQUASHFS_SWAP_FRAGMENT_INDEXES_3(s, d, n) SQUASHFS_SWAP_LONG_LONGS_3(s, d, n)
+#define SQUASHFS_SWAP_LOOKUP_BLOCKS_3(s, d, n) SQUASHFS_SWAP_LONG_LONGS_3(s, d, n)
+
+#define SQUASHFS_SWAP_FRAGMENT_ENTRY_3(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_3));\
+       SQUASHFS_SWAP((s)->start_block, d, 0, 64);\
+       SQUASHFS_SWAP((s)->size, d, 64, 32);\
+}
+
+/* fragment and fragment table defines */
+#define SQUASHFS_FRAGMENT_BYTES_3(A)   ((A) * sizeof(struct squashfs_fragment_entry_3))
+
+#define SQUASHFS_FRAGMENT_INDEX_3(A)   (SQUASHFS_FRAGMENT_BYTES_3(A) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_OFFSET_3(A)    (SQUASHFS_FRAGMENT_BYTES_3(A) % \
+                                               SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEXES_3(A) ((SQUASHFS_FRAGMENT_BYTES_3(A) + \
+                                       SQUASHFS_METADATA_SIZE - 1) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_BYTES_3(A)     (SQUASHFS_FRAGMENT_INDEXES_3(A) *\
+                                               sizeof(long long))
+
+/* inode lookup table defines */
+#define SQUASHFS_LOOKUP_BYTES_3(A)     ((A) * sizeof(squashfs_inode))
+
+#define SQUASHFS_LOOKUP_BLOCK_3(A)             (SQUASHFS_LOOKUP_BYTES_3(A) / \
+                                               SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_LOOKUP_BLOCK_OFFSET_3(A)      (SQUASHFS_LOOKUP_BYTES_3(A) % \
+                                               SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_LOOKUP_BLOCKS_3(A)    ((SQUASHFS_LOOKUP_BYTES_3(A) + \
+                                       SQUASHFS_METADATA_SIZE - 1) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_LOOKUP_BLOCK_BYTES_3(A)       (SQUASHFS_LOOKUP_BLOCKS(A) *\
+                                       sizeof(long long))
+
+/*
+ * definitions for structures on disk - layout 1.x
+ */
+#define SQUASHFS_TYPES                 5
+#define SQUASHFS_IPC_TYPE              0
+
+struct squashfs_base_inode_header_1 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:4; /* index into uid table */
+       unsigned int            guid:4; /* index into guid table */
+} __attribute__ ((packed));
+
+struct squashfs_ipc_inode_header_1 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:4; /* index into uid table */
+       unsigned int            guid:4; /* index into guid table */
+       unsigned int            type:4;
+       unsigned int            offset:4;
+} __attribute__ ((packed));
+
+struct squashfs_dev_inode_header_1 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:4; /* index into uid table */
+       unsigned int            guid:4; /* index into guid table */
+       unsigned short          rdev;
+} __attribute__ ((packed));
+       
+struct squashfs_symlink_inode_header_1 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:4; /* index into uid table */
+       unsigned int            guid:4; /* index into guid table */
+       unsigned short          symlink_size;
+       char                    symlink[0];
+} __attribute__ ((packed));
+
+struct squashfs_reg_inode_header_1 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:4; /* index into uid table */
+       unsigned int            guid:4; /* index into guid table */
+       int                     mtime;
+       unsigned int            start_block;
+       unsigned int            file_size:32;
+       unsigned short          block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_inode_header_1 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:4; /* index into uid table */
+       unsigned int            guid:4; /* index into guid table */
+       unsigned int            file_size:19;
+       unsigned int            offset:13;
+       int                     mtime;
+       unsigned int            start_block:24;
+} __attribute__  ((packed));
+
+union squashfs_inode_header_1 {
+       struct squashfs_base_inode_header_1     base;
+       struct squashfs_dev_inode_header_1      dev;
+       struct squashfs_symlink_inode_header_1  symlink;
+       struct squashfs_reg_inode_header_1      reg;
+       struct squashfs_dir_inode_header_1      dir;
+       struct squashfs_ipc_inode_header_1      ipc;
+};
+
+typedef struct squashfs_dir_index_1 squashfs_dir_index_1;
+typedef struct squashfs_base_inode_header_1 squashfs_base_inode_header_1;
+typedef struct squashfs_ipc_inode_header_1 squashfs_ipc_inode_header_1;
+typedef struct squashfs_dev_inode_header_1 squashfs_dev_inode_header_1;
+typedef struct squashfs_symlink_inode_header_1 squashfs_symlink_inode_header_1;
+typedef struct squashfs_reg_inode_header_1 squashfs_reg_inode_header_1;
+typedef struct squashfs_dir_inode_header_1 squashfs_dir_inode_header_1;
+
+#define SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n) \
+       SQUASHFS_MEMSET(s, d, n);\
+       SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
+       SQUASHFS_SWAP((s)->mode, d, 4, 12);\
+       SQUASHFS_SWAP((s)->uid, d, 16, 4);\
+       SQUASHFS_SWAP((s)->guid, d, 20, 4);
+
+#define SQUASHFS_SWAP_BASE_INODE_HEADER_1(s, d, n) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, n)\
+}
+
+#define SQUASHFS_SWAP_IPC_INODE_HEADER_1(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+                       sizeof(struct squashfs_ipc_inode_header_1));\
+       SQUASHFS_SWAP((s)->type, d, 24, 4);\
+       SQUASHFS_SWAP((s)->offset, d, 28, 4);\
+}
+
+#define SQUASHFS_SWAP_DEV_INODE_HEADER_1(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+                       sizeof(struct squashfs_dev_inode_header_1));\
+       SQUASHFS_SWAP((s)->rdev, d, 24, 16);\
+}
+
+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+                       sizeof(struct squashfs_symlink_inode_header_1));\
+       SQUASHFS_SWAP((s)->symlink_size, d, 24, 16);\
+}
+
+#define SQUASHFS_SWAP_REG_INODE_HEADER_1(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+                       sizeof(struct squashfs_reg_inode_header_1));\
+       SQUASHFS_SWAP((s)->mtime, d, 24, 32);\
+       SQUASHFS_SWAP((s)->start_block, d, 56, 32);\
+       SQUASHFS_SWAP((s)->file_size, d, 88, 32);\
+}
+
+#define SQUASHFS_SWAP_DIR_INODE_HEADER_1(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_1(s, d, \
+                       sizeof(struct squashfs_dir_inode_header_1));\
+       SQUASHFS_SWAP((s)->file_size, d, 24, 19);\
+       SQUASHFS_SWAP((s)->offset, d, 43, 13);\
+       SQUASHFS_SWAP((s)->mtime, d, 56, 32);\
+       SQUASHFS_SWAP((s)->start_block, d, 88, 24);\
+}
+
+/*
+ * definitions for structures on disk - layout 2.x
+ */
+struct squashfs_dir_index_2 {
+       unsigned int            index:27;
+       unsigned int            start_block:29;
+       unsigned char           size;
+       unsigned char           name[0];
+} __attribute__ ((packed));
+
+struct squashfs_base_inode_header_2 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:8; /* index into uid table */
+       unsigned int            guid:8; /* index into guid table */
+} __attribute__ ((packed));
+
+struct squashfs_ipc_inode_header_2 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:8; /* index into uid table */
+       unsigned int            guid:8; /* index into guid table */
+} __attribute__ ((packed));
+
+struct squashfs_dev_inode_header_2 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:8; /* index into uid table */
+       unsigned int            guid:8; /* index into guid table */
+       unsigned short          rdev;
+} __attribute__ ((packed));
+       
+struct squashfs_symlink_inode_header_2 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:8; /* index into uid table */
+       unsigned int            guid:8; /* index into guid table */
+       unsigned short          symlink_size;
+       char                    symlink[0];
+} __attribute__ ((packed));
+
+struct squashfs_reg_inode_header_2 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:8; /* index into uid table */
+       unsigned int            guid:8; /* index into guid table */
+       int                     mtime;
+       unsigned int            start_block;
+       unsigned int            fragment;
+       unsigned int            offset;
+       unsigned int            file_size:32;
+       unsigned short          block_list[0];
+} __attribute__ ((packed));
+
+struct squashfs_dir_inode_header_2 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:8; /* index into uid table */
+       unsigned int            guid:8; /* index into guid table */
+       unsigned int            file_size:19;
+       unsigned int            offset:13;
+       int                     mtime;
+       unsigned int            start_block:24;
+} __attribute__  ((packed));
+
+struct squashfs_ldir_inode_header_2 {
+       unsigned int            inode_type:4;
+       unsigned int            mode:12; /* protection */
+       unsigned int            uid:8; /* index into uid table */
+       unsigned int            guid:8; /* index into guid table */
+       unsigned int            file_size:27;
+       unsigned int            offset:13;
+       int                     mtime;
+       unsigned int            start_block:24;
+       unsigned int            i_count:16;
+       struct squashfs_dir_index_2     index[0];
+} __attribute__  ((packed));
+
+union squashfs_inode_header_2 {
+       struct squashfs_base_inode_header_2     base;
+       struct squashfs_dev_inode_header_2      dev;
+       struct squashfs_symlink_inode_header_2  symlink;
+       struct squashfs_reg_inode_header_2      reg;
+       struct squashfs_dir_inode_header_2      dir;
+       struct squashfs_ldir_inode_header_2     ldir;
+       struct squashfs_ipc_inode_header_2      ipc;
+};
+       
+struct squashfs_dir_header_2 {
+       unsigned int            count:8;
+       unsigned int            start_block:24;
+} __attribute__ ((packed));
+
+struct squashfs_dir_entry_2 {
+       unsigned int            offset:13;
+       unsigned int            type:3;
+       unsigned int            size:8;
+       char                    name[0];
+} __attribute__ ((packed));
+
+struct squashfs_fragment_entry_2 {
+       unsigned int            start_block;
+       unsigned int            size;
+} __attribute__ ((packed));
+
+typedef struct squashfs_dir_index_2 squashfs_dir_index_2;
+typedef struct squashfs_base_inode_header_2 squashfs_base_inode_header_2;
+typedef struct squashfs_ipc_inode_header_2 squashfs_ipc_inode_header_2;
+typedef struct squashfs_dev_inode_header_2 squashfs_dev_inode_header_2;
+typedef struct squashfs_symlink_inode_header_2 squashfs_symlink_inode_header_2;
+typedef struct squashfs_reg_inode_header_2 squashfs_reg_inode_header_2;
+typedef struct squashfs_lreg_inode_header_2 squashfs_lreg_inode_header_2;
+typedef struct squashfs_dir_inode_header_2 squashfs_dir_inode_header_2;
+typedef struct squashfs_ldir_inode_header_2 squashfs_ldir_inode_header_2;
+typedef struct squashfs_dir_entry_2 squashfs_dir_entry_2;
+typedef struct squashfs_dir_header_2 squashfs_dir_header_2;
+typedef struct squashfs_fragment_entry_2 squashfs_fragment_entry_2;
+
+#define SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
+       SQUASHFS_MEMSET(s, d, n);\
+       SQUASHFS_SWAP((s)->inode_type, d, 0, 4);\
+       SQUASHFS_SWAP((s)->mode, d, 4, 12);\
+       SQUASHFS_SWAP((s)->uid, d, 16, 8);\
+       SQUASHFS_SWAP((s)->guid, d, 24, 8);\
+
+#define SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, n) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, n)\
+}
+
+#define SQUASHFS_SWAP_IPC_INODE_HEADER_2(s, d) \
+       SQUASHFS_SWAP_BASE_INODE_HEADER_2(s, d, sizeof(struct squashfs_ipc_inode_header_2))
+
+#define SQUASHFS_SWAP_DEV_INODE_HEADER_2(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+                       sizeof(struct squashfs_dev_inode_header_2)); \
+       SQUASHFS_SWAP((s)->rdev, d, 32, 16);\
+}
+
+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+                       sizeof(struct squashfs_symlink_inode_header_2));\
+       SQUASHFS_SWAP((s)->symlink_size, d, 32, 16);\
+}
+
+#define SQUASHFS_SWAP_REG_INODE_HEADER_2(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+                       sizeof(struct squashfs_reg_inode_header_2));\
+       SQUASHFS_SWAP((s)->mtime, d, 32, 32);\
+       SQUASHFS_SWAP((s)->start_block, d, 64, 32);\
+       SQUASHFS_SWAP((s)->fragment, d, 96, 32);\
+       SQUASHFS_SWAP((s)->offset, d, 128, 32);\
+       SQUASHFS_SWAP((s)->file_size, d, 160, 32);\
+}
+
+#define SQUASHFS_SWAP_DIR_INODE_HEADER_2(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+                       sizeof(struct squashfs_dir_inode_header_2));\
+       SQUASHFS_SWAP((s)->file_size, d, 32, 19);\
+       SQUASHFS_SWAP((s)->offset, d, 51, 13);\
+       SQUASHFS_SWAP((s)->mtime, d, 64, 32);\
+       SQUASHFS_SWAP((s)->start_block, d, 96, 24);\
+}
+
+#define SQUASHFS_SWAP_LDIR_INODE_HEADER_2(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_SWAP_BASE_INODE_CORE_2(s, d, \
+                       sizeof(struct squashfs_ldir_inode_header_2));\
+       SQUASHFS_SWAP((s)->file_size, d, 32, 27);\
+       SQUASHFS_SWAP((s)->offset, d, 59, 13);\
+       SQUASHFS_SWAP((s)->mtime, d, 72, 32);\
+       SQUASHFS_SWAP((s)->start_block, d, 104, 24);\
+       SQUASHFS_SWAP((s)->i_count, d, 128, 16);\
+}
+
+#define SQUASHFS_SWAP_DIR_INDEX_2(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_index_2));\
+       SQUASHFS_SWAP((s)->index, d, 0, 27);\
+       SQUASHFS_SWAP((s)->start_block, d, 27, 29);\
+       SQUASHFS_SWAP((s)->size, d, 56, 8);\
+}
+#define SQUASHFS_SWAP_DIR_HEADER_2(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_header_2));\
+       SQUASHFS_SWAP((s)->count, d, 0, 8);\
+       SQUASHFS_SWAP((s)->start_block, d, 8, 24);\
+}
+
+#define SQUASHFS_SWAP_DIR_ENTRY_2(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_dir_entry_2));\
+       SQUASHFS_SWAP((s)->offset, d, 0, 13);\
+       SQUASHFS_SWAP((s)->type, d, 13, 3);\
+       SQUASHFS_SWAP((s)->size, d, 16, 8);\
+}
+
+#define SQUASHFS_SWAP_FRAGMENT_ENTRY_2(s, d) {\
+       SQUASHFS_SWAP_START\
+       SQUASHFS_MEMSET(s, d, sizeof(struct squashfs_fragment_entry_2));\
+       SQUASHFS_SWAP((s)->start_block, d, 0, 32);\
+       SQUASHFS_SWAP((s)->size, d, 32, 32);\
+}
+
+#define SQUASHFS_SWAP_FRAGMENT_INDEXES_2(s, d, n) SQUASHFS_SWAP_INTS_3(s, d, n)
+
+/* fragment and fragment table defines */
+#define SQUASHFS_FRAGMENT_BYTES_2(A)   ((A) * sizeof(struct squashfs_fragment_entry_2))
+
+#define SQUASHFS_FRAGMENT_INDEX_2(A)   (SQUASHFS_FRAGMENT_BYTES_2(A) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_OFFSET_2(A)    (SQUASHFS_FRAGMENT_BYTES_2(A) % \
+                                               SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEXES_2(A) ((SQUASHFS_FRAGMENT_BYTES_2(A) + \
+                                       SQUASHFS_METADATA_SIZE - 1) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_BYTES_2(A)     (SQUASHFS_FRAGMENT_INDEXES_2(A) *\
+                                               sizeof(int))
+/*
+ * macros used to swap each structure entry, taking into account
+ * bitfields and different bitfield placing conventions on differing architectures
+ */
+#if __BYTE_ORDER == __BIG_ENDIAN
+       /* convert from little endian to big endian */
+#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, tbits, b_pos)
+#else
+       /* convert from big endian to little endian */
+#define SQUASHFS_SWAP(value, p, pos, tbits) _SQUASHFS_SWAP(value, p, pos, tbits, 64 - tbits - b_pos)
+#endif
+
+#define _SQUASHFS_SWAP(value, p, pos, tbits, SHIFT) {\
+       b_pos = pos % 8;\
+       val = 0;\
+       s = (unsigned char *)p + (pos / 8);\
+       d = ((unsigned char *) &val) + 7;\
+       for(bits = 0; bits < (tbits + b_pos); bits += 8) \
+               *d-- = *s++;\
+       value = (val >> (SHIFT));\
+}
+#define SQUASHFS_MEMSET(s, d, n)       memset(s, 0, n);
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/squashfs_fs.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/squashfs_fs.h
new file mode 100644 (file)
index 0000000..79dfae4
--- /dev/null
@@ -0,0 +1,499 @@
+#ifndef SQUASHFS_FS
+#define SQUASHFS_FS
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2012,
+ * 2013, 2014, 2017, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * squashfs_fs.h
+ */
+
+#define SQUASHFS_CACHED_FRAGMENTS      CONFIG_SQUASHFS_FRAGMENT_CACHE_SIZE     
+#define SQUASHFS_MAJOR                 4
+#define SQUASHFS_MINOR                 0
+#define SQUASHFS_MAGIC                 0x73717368
+#define SQUASHFS_MAGIC_SWAP            0x68737173
+#define SQUASHFS_START                 0
+
+/* size of metadata (inode and directory) blocks */
+#define SQUASHFS_METADATA_SIZE         8192
+#define SQUASHFS_METADATA_LOG          13
+
+/* default size of data blocks */
+#define SQUASHFS_FILE_SIZE             131072
+
+#define SQUASHFS_FILE_MAX_SIZE         1048576
+#define SQUASHFS_FILE_MAX_LOG          20
+
+/* Max number of uids and gids */
+#define SQUASHFS_IDS                   65536
+
+/* Max length of filename (not 255) */
+#define SQUASHFS_NAME_LEN              256
+
+/* Max value for directory header count */
+#define SQUASHFS_DIR_COUNT             256
+
+#define SQUASHFS_INVALID               ((long long) 0xffffffffffff)
+#define SQUASHFS_INVALID_FRAG          ((unsigned int) 0xffffffff)
+#define SQUASHFS_INVALID_XATTR         ((unsigned int) 0xffffffff)
+#define SQUASHFS_INVALID_BLK           ((long long) -1)
+#define SQUASHFS_USED_BLK              ((long long) -2)
+
+/* Filesystem flags */
+#define SQUASHFS_NOI                   0
+#define SQUASHFS_NOD                   1
+#define SQUASHFS_CHECK                 2
+#define SQUASHFS_NOF                   3
+#define SQUASHFS_NO_FRAG               4
+#define SQUASHFS_ALWAYS_FRAG           5
+#define SQUASHFS_DUPLICATE             6
+#define SQUASHFS_EXPORT                        7
+#define SQUASHFS_NOX                   8
+#define SQUASHFS_NO_XATTR              9
+#define SQUASHFS_COMP_OPT              10
+#define SQUASHFS_NOID                  11
+
+#define SQUASHFS_BIT(flag, bit)                ((flag >> bit) & 1)
+
+#define SQUASHFS_UNCOMPRESSED_INODES(flags)    SQUASHFS_BIT(flags, \
+                                               SQUASHFS_NOI)
+
+#define SQUASHFS_UNCOMPRESSED_DATA(flags)      SQUASHFS_BIT(flags, \
+                                               SQUASHFS_NOD)
+
+#define SQUASHFS_UNCOMPRESSED_FRAGMENTS(flags) SQUASHFS_BIT(flags, \
+                                               SQUASHFS_NOF)
+
+#define SQUASHFS_NO_FRAGMENTS(flags)           SQUASHFS_BIT(flags, \
+                                               SQUASHFS_NO_FRAG)
+
+#define SQUASHFS_ALWAYS_FRAGMENTS(flags)       SQUASHFS_BIT(flags, \
+                                               SQUASHFS_ALWAYS_FRAG)
+
+#define SQUASHFS_DUPLICATES(flags)             SQUASHFS_BIT(flags, \
+                                               SQUASHFS_DUPLICATE)
+
+#define SQUASHFS_EXPORTABLE(flags)             SQUASHFS_BIT(flags, \
+                                               SQUASHFS_EXPORT)
+
+#define SQUASHFS_UNCOMPRESSED_XATTRS(flags)    SQUASHFS_BIT(flags, \
+                                               SQUASHFS_NOX)
+
+#define SQUASHFS_NO_XATTRS(flags)              SQUASHFS_BIT(flags, \
+                                               SQUASHFS_NO_XATTR)
+
+#define SQUASHFS_COMP_OPTS(flags)              SQUASHFS_BIT(flags, \
+                                               SQUASHFS_COMP_OPT)
+
+#define SQUASHFS_UNCOMPRESSED_IDS(flags)       SQUASHFS_BIT(flags, \
+                                               SQUASHFS_NOID)
+
+#define SQUASHFS_MKFLAGS(noi, nod, nof, nox, noid, no_frag, always_frag, \
+               duplicate_checking, exportable, no_xattr, comp_opt) (noi | \
+               (nod << 1) | (nof << 3) | (no_frag << 4) | \
+               (always_frag << 5) | (duplicate_checking << 6) | \
+               (exportable << 7) | (nox << 8) | (no_xattr << 9) | \
+               (comp_opt << 10) | (noid << 11))
+
+/* Max number of types and file types */
+#define SQUASHFS_DIR_TYPE              1
+#define SQUASHFS_FILE_TYPE             2
+#define SQUASHFS_SYMLINK_TYPE          3
+#define SQUASHFS_BLKDEV_TYPE           4
+#define SQUASHFS_CHRDEV_TYPE           5
+#define SQUASHFS_FIFO_TYPE             6
+#define SQUASHFS_SOCKET_TYPE           7
+#define SQUASHFS_LDIR_TYPE             8
+#define SQUASHFS_LREG_TYPE             9
+#define SQUASHFS_LSYMLINK_TYPE         10
+#define SQUASHFS_LBLKDEV_TYPE          11
+#define SQUASHFS_LCHRDEV_TYPE          12
+#define SQUASHFS_LFIFO_TYPE            13
+#define SQUASHFS_LSOCKET_TYPE          14
+
+/* Xattr types */
+#define SQUASHFS_XATTR_USER            0
+#define SQUASHFS_XATTR_TRUSTED         1
+#define SQUASHFS_XATTR_SECURITY                2
+#define SQUASHFS_XATTR_VALUE_OOL       256
+#define SQUASHFS_XATTR_PREFIX_MASK     0xff
+
+/* Flag whether block is compressed or uncompressed, bit is set if block is
+ * uncompressed */
+#define SQUASHFS_COMPRESSED_BIT                (1 << 15)
+
+#define SQUASHFS_COMPRESSED_SIZE(B)    (((B) & ~SQUASHFS_COMPRESSED_BIT) ? \
+               (B) & ~SQUASHFS_COMPRESSED_BIT :  SQUASHFS_COMPRESSED_BIT)
+
+#define SQUASHFS_COMPRESSED(B)         (!((B) & SQUASHFS_COMPRESSED_BIT))
+
+#define SQUASHFS_COMPRESSED_BIT_BLOCK          (1 << 24)
+
+#define SQUASHFS_COMPRESSED_SIZE_BLOCK(B)      ((B) & \
+       ~SQUASHFS_COMPRESSED_BIT_BLOCK)
+
+#define SQUASHFS_COMPRESSED_BLOCK(B)   (!((B) & SQUASHFS_COMPRESSED_BIT_BLOCK))
+
+/*
+ * Inode number ops.  Inodes consist of a compressed block number, and an
+ * uncompressed  offset within that block
+ */
+#define SQUASHFS_INODE_BLK(a)          ((unsigned int) ((a) >> 16))
+
+#define SQUASHFS_INODE_OFFSET(a)       ((unsigned int) ((a) & 0xffff))
+
+#define SQUASHFS_MKINODE(A, B)         ((squashfs_inode)(((squashfs_inode) (A)\
+                                       << 16) + (B)))
+
+/* Compute 32 bit VFS inode number from squashfs inode number */
+#define SQUASHFS_MK_VFS_INODE(a, b)    ((unsigned int) (((a) << 8) + \
+                                       ((b) >> 2) + 1))
+
+/* Translate between VFS mode and squashfs mode */
+#define SQUASHFS_MODE(a)               ((a) & 0xfff)
+
+/* fragment and fragment table defines */
+#define SQUASHFS_FRAGMENT_BYTES(A)     ((A) * \
+                                       sizeof(struct squashfs_fragment_entry))
+
+#define SQUASHFS_FRAGMENT_INDEX(A)     (SQUASHFS_FRAGMENT_BYTES(A) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_OFFSET(A)      (SQUASHFS_FRAGMENT_BYTES(A) % \
+                                               SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEXES(A)   ((SQUASHFS_FRAGMENT_BYTES(A) + \
+                                       SQUASHFS_METADATA_SIZE - 1) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_FRAGMENT_INDEX_BYTES(A)       (SQUASHFS_FRAGMENT_INDEXES(A) *\
+                                               sizeof(long long))
+
+/* inode lookup table defines */
+#define SQUASHFS_LOOKUP_BYTES(A)       ((A) * sizeof(squashfs_inode))
+
+#define SQUASHFS_LOOKUP_BLOCK(A)               (SQUASHFS_LOOKUP_BYTES(A) / \
+                                               SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_LOOKUP_BLOCK_OFFSET(A)                (SQUASHFS_LOOKUP_BYTES(A) % \
+                                               SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_LOOKUP_BLOCKS(A)      ((SQUASHFS_LOOKUP_BYTES(A) + \
+                                       SQUASHFS_METADATA_SIZE - 1) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_LOOKUP_BLOCK_BYTES(A) (SQUASHFS_LOOKUP_BLOCKS(A) *\
+                                       sizeof(long long))
+
+/* uid lookup table defines */
+#define SQUASHFS_ID_BYTES(A)   ((A) * sizeof(unsigned int))
+
+#define SQUASHFS_ID_BLOCK(A)           (SQUASHFS_ID_BYTES(A) / \
+                                               SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_ID_BLOCK_OFFSET(A)            (SQUASHFS_ID_BYTES(A) % \
+                                               SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_ID_BLOCKS(A)  ((SQUASHFS_ID_BYTES(A) + \
+                                       SQUASHFS_METADATA_SIZE - 1) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_ID_BLOCK_BYTES(A)     (SQUASHFS_ID_BLOCKS(A) *\
+                                       sizeof(long long))
+
+/* xattr id lookup table defines */
+#define SQUASHFS_XATTR_BYTES(A)                ((A) * sizeof(struct squashfs_xattr_id))
+
+#define SQUASHFS_XATTR_BLOCK(A)                (SQUASHFS_XATTR_BYTES(A) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_XATTR_BLOCK_OFFSET(A) (SQUASHFS_XATTR_BYTES(A) % \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_XATTR_BLOCKS(A)       ((SQUASHFS_XATTR_BYTES(A) + \
+                                       SQUASHFS_METADATA_SIZE - 1) / \
+                                       SQUASHFS_METADATA_SIZE)
+
+#define SQUASHFS_XATTR_BLOCK_BYTES(A)  (SQUASHFS_XATTR_BLOCKS(A) *\
+                                       sizeof(long long))
+
+#define SQUASHFS_XATTR_BLK(A)          ((unsigned int) ((A) >> 16))
+
+#define SQUASHFS_XATTR_OFFSET(A)       ((unsigned int) ((A) & 0xffff))
+
+/* cached data constants for filesystem */
+#define SQUASHFS_CACHED_BLKS           8
+
+#define SQUASHFS_MAX_FILE_SIZE_LOG     64
+
+#define SQUASHFS_MAX_FILE_SIZE         ((long long) 1 << \
+                                       (SQUASHFS_MAX_FILE_SIZE_LOG - 2))
+
+#define SQUASHFS_MARKER_BYTE           0xff
+
+/* meta index cache */
+#define SQUASHFS_META_INDEXES  (SQUASHFS_METADATA_SIZE / sizeof(unsigned int))
+#define SQUASHFS_META_ENTRIES  31
+#define SQUASHFS_META_NUMBER   8
+#define SQUASHFS_SLOTS         4
+
+struct meta_entry {
+       long long               data_block;
+       unsigned int            index_block;
+       unsigned short          offset;
+       unsigned short          pad;
+};
+
+struct meta_index {
+       unsigned int            inode_number;
+       unsigned int            offset;
+       unsigned short          entries;
+       unsigned short          skip;
+       unsigned short          locked;
+       unsigned short          pad;
+       struct meta_entry       meta_entry[SQUASHFS_META_ENTRIES];
+};
+
+
+/*
+ * definitions for structures on disk
+ */
+
+typedef long long              squashfs_block;
+typedef long long              squashfs_inode;
+
+#define ZLIB_COMPRESSION       1
+#define LZMA_COMPRESSION       2
+#define LZO_COMPRESSION                3
+#define XZ_COMPRESSION         4
+#define LZ4_COMPRESSION                5
+#define ZSTD_COMPRESSION       6
+
+struct squashfs_super_block {
+       unsigned int            s_magic;
+       unsigned int            inodes;
+       unsigned int            mkfs_time /* time of filesystem creation */;
+       unsigned int            block_size;
+       unsigned int            fragments;
+       unsigned short          compression;
+       unsigned short          block_log;
+       unsigned short          flags;
+       unsigned short          no_ids;
+       unsigned short          s_major;
+       unsigned short          s_minor;
+       squashfs_inode          root_inode;
+       long long               bytes_used;
+       long long               id_table_start;
+       long long               xattr_id_table_start;
+       long long               inode_table_start;
+       long long               directory_table_start;
+       long long               fragment_table_start;
+       long long               lookup_table_start;
+};
+
+struct squashfs_dir_index {
+       unsigned int            index;
+       unsigned int            start_block;
+       unsigned int            size;
+       unsigned char           name[0];
+};
+
+struct squashfs_base_inode_header {
+       unsigned short          inode_type;
+       unsigned short          mode;
+       unsigned short          uid;
+       unsigned short          guid;
+       unsigned int            mtime;
+       unsigned int            inode_number;
+};
+
+struct squashfs_ipc_inode_header {
+       unsigned short          inode_type;
+       unsigned short          mode;
+       unsigned short          uid;
+       unsigned short          guid;
+       unsigned int            mtime;
+       unsigned int            inode_number;
+       unsigned int            nlink;
+};
+
+struct squashfs_lipc_inode_header {
+       unsigned short          inode_type;
+       unsigned short          mode;
+       unsigned short          uid;
+       unsigned short          guid;
+       unsigned int            mtime;
+       unsigned int            inode_number;
+       unsigned int            nlink;
+       unsigned int            xattr;
+};
+
+struct squashfs_dev_inode_header {
+       unsigned short          inode_type;
+       unsigned short          mode;
+       unsigned short          uid;
+       unsigned short          guid;
+       unsigned int            mtime;
+       unsigned int            inode_number;
+       unsigned int            nlink;
+       unsigned int            rdev;
+};
+       
+struct squashfs_ldev_inode_header {
+       unsigned short          inode_type;
+       unsigned short          mode;
+       unsigned short          uid;
+       unsigned short          guid;
+       unsigned int            mtime;
+       unsigned int            inode_number;
+       unsigned int            nlink;
+       unsigned int            rdev;
+       unsigned int            xattr;
+};
+       
+struct squashfs_symlink_inode_header {
+       unsigned short          inode_type;
+       unsigned short          mode;
+       unsigned short          uid;
+       unsigned short          guid;
+       unsigned int            mtime;
+       unsigned int            inode_number;
+       unsigned int            nlink;
+       unsigned int            symlink_size;
+       char                    symlink[0];
+};
+
+struct squashfs_reg_inode_header {
+       unsigned short          inode_type;
+       unsigned short          mode;
+       unsigned short          uid;
+       unsigned short          guid;
+       unsigned int            mtime;
+       unsigned int            inode_number;
+       unsigned int            start_block;
+       unsigned int            fragment;
+       unsigned int            offset;
+       unsigned int            file_size;
+       unsigned int            block_list[0];
+};
+
+struct squashfs_lreg_inode_header {
+       unsigned short          inode_type;
+       unsigned short          mode;
+       unsigned short          uid;
+       unsigned short          guid;
+       unsigned int            mtime;
+       unsigned int            inode_number;
+       squashfs_block          start_block;
+       long long               file_size;
+       long long               sparse;
+       unsigned int            nlink;
+       unsigned int            fragment;
+       unsigned int            offset;
+       unsigned int            xattr;
+       unsigned int            block_list[0];
+};
+
+struct squashfs_dir_inode_header {
+       unsigned short          inode_type;
+       unsigned short          mode;
+       unsigned short          uid;
+       unsigned short          guid;
+       unsigned int            mtime;
+       unsigned int            inode_number;
+       unsigned int            start_block;
+       unsigned int            nlink;
+       unsigned short          file_size;
+       unsigned short          offset;
+       unsigned int            parent_inode;
+};
+
+struct squashfs_ldir_inode_header {
+       unsigned short          inode_type;
+       unsigned short          mode;
+       unsigned short          uid;
+       unsigned short          guid;
+       unsigned int            mtime;
+       unsigned int            inode_number;
+       unsigned int            nlink;
+       unsigned int            file_size;
+       unsigned int            start_block;
+       unsigned int            parent_inode;
+       unsigned short          i_count;
+       unsigned short          offset;
+       unsigned int            xattr;
+       struct squashfs_dir_index       index[0];
+};
+
+union squashfs_inode_header {
+       struct squashfs_base_inode_header       base;
+       struct squashfs_dev_inode_header        dev;
+       struct squashfs_ldev_inode_header       ldev;
+       struct squashfs_symlink_inode_header    symlink;
+       struct squashfs_reg_inode_header        reg;
+       struct squashfs_lreg_inode_header       lreg;
+       struct squashfs_dir_inode_header        dir;
+       struct squashfs_ldir_inode_header       ldir;
+       struct squashfs_ipc_inode_header        ipc;
+       struct squashfs_lipc_inode_header       lipc;
+};
+       
+struct squashfs_dir_entry {
+       unsigned short          offset;
+       short                   inode_number;
+       unsigned short          type;
+       unsigned short          size;
+       char                    name[0];
+};
+
+struct squashfs_dir_header {
+       unsigned int            count;
+       unsigned int            start_block;
+       unsigned int            inode_number;
+};
+
+struct squashfs_fragment_entry {
+       long long               start_block;
+       unsigned int            size;
+       unsigned int            unused;
+};
+
+struct squashfs_xattr_entry {
+       unsigned short          type;
+       unsigned short          size;
+};
+
+struct squashfs_xattr_val {
+       unsigned int            vsize;
+};
+
+struct squashfs_xattr_id {
+       long long               xattr;
+       unsigned int            count;
+       unsigned int            size;
+};
+
+struct squashfs_xattr_table {
+       long long               xattr_table_start;
+       unsigned int            xattr_ids;
+       unsigned int            unused;
+};
+
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/squashfs_swap.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/squashfs_swap.h
new file mode 100644 (file)
index 0000000..271dd21
--- /dev/null
@@ -0,0 +1,423 @@
+#ifndef SQUASHFS_SWAP_H
+#define SQUASHFS_SWAP_H
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2008, 2009, 2010, 2013, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * squashfs_swap.h
+ */
+
+/*
+ * macros to convert each stucture from big endian to little endian
+ */
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+#include <stddef.h>
+extern void swap_le16(void *, void *);
+extern void swap_le32(void *, void *);
+extern void swap_le64(void *, void *);
+extern void swap_le16_num(void *, void *, int);
+extern void swap_le32_num(void *, void *, int);
+extern void swap_le64_num(void *, void *, int);
+extern unsigned short inswap_le16(unsigned short);
+extern unsigned int inswap_le32(unsigned int);
+extern long long inswap_le64(long long);
+extern void inswap_le16_num(unsigned short *, int);
+extern void inswap_le32_num(unsigned int *, int);
+extern void inswap_le64_num(long long *, int);
+
+#define _SQUASHFS_SWAP_SUPER_BLOCK(s, d, SWAP_FUNC) {\
+       SWAP_FUNC(32, s, d, s_magic, struct squashfs_super_block);\
+       SWAP_FUNC(32, s, d, inodes, struct squashfs_super_block);\
+       SWAP_FUNC(32, s, d, mkfs_time, struct squashfs_super_block);\
+       SWAP_FUNC(32, s, d, block_size, struct squashfs_super_block);\
+       SWAP_FUNC(32, s, d, fragments, struct squashfs_super_block);\
+       SWAP_FUNC(16, s, d, compression, struct squashfs_super_block);\
+       SWAP_FUNC(16, s, d, block_log, struct squashfs_super_block);\
+       SWAP_FUNC(16, s, d, flags, struct squashfs_super_block);\
+       SWAP_FUNC(16, s, d, no_ids, struct squashfs_super_block);\
+       SWAP_FUNC(16, s, d, s_major, struct squashfs_super_block);\
+       SWAP_FUNC(16, s, d, s_minor, struct squashfs_super_block);\
+       SWAP_FUNC(64, s, d, root_inode, struct squashfs_super_block);\
+       SWAP_FUNC(64, s, d, bytes_used, struct squashfs_super_block);\
+       SWAP_FUNC(64, s, d, id_table_start, struct squashfs_super_block);\
+       SWAP_FUNC(64, s, d, xattr_id_table_start, struct squashfs_super_block);\
+       SWAP_FUNC(64, s, d, inode_table_start, struct squashfs_super_block);\
+       SWAP_FUNC(64, s, d, directory_table_start, struct squashfs_super_block);\
+       SWAP_FUNC(64, s, d, fragment_table_start, struct squashfs_super_block);\
+       SWAP_FUNC(64, s, d, lookup_table_start, struct squashfs_super_block);\
+}
+
+#define _SQUASHFS_SWAP_DIR_INDEX(s, d, SWAP_FUNC) {\
+       SWAP_FUNC(32, s, d, index, struct squashfs_dir_index);\
+       SWAP_FUNC(32, s, d, start_block, struct squashfs_dir_index);\
+       SWAP_FUNC(32, s, d, size, struct squashfs_dir_index);\
+}
+
+#define _SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, SWAP_FUNC) {\
+       SWAP_FUNC(16, s, d, inode_type, struct squashfs_base_inode_header);\
+       SWAP_FUNC(16, s, d, mode, struct squashfs_base_inode_header);\
+       SWAP_FUNC(16, s, d, uid, struct squashfs_base_inode_header);\
+       SWAP_FUNC(16, s, d, guid, struct squashfs_base_inode_header);\
+       SWAP_FUNC(32, s, d, mtime, struct squashfs_base_inode_header);\
+       SWAP_FUNC(32, s, d, inode_number, struct squashfs_base_inode_header);\
+}
+
+#define _SQUASHFS_SWAP_IPC_INODE_HEADER(s, d, SWAP_FUNC) {\
+       SWAP_FUNC(16, s, d, inode_type, struct squashfs_ipc_inode_header);\
+       SWAP_FUNC(16, s, d, mode, struct squashfs_ipc_inode_header);\
+       SWAP_FUNC(16, s, d, uid, struct squashfs_ipc_inode_header);\
+       SWAP_FUNC(16, s, d, guid, struct squashfs_ipc_inode_header);\
+       SWAP_FUNC(32, s, d, mtime, struct squashfs_ipc_inode_header);\
+       SWAP_FUNC(32, s, d, inode_number, struct squashfs_ipc_inode_header);\
+       SWAP_FUNC(32, s, d, nlink, struct squashfs_ipc_inode_header);\
+}
+
+#define _SQUASHFS_SWAP_LIPC_INODE_HEADER(s, d, SWAP_FUNC) {\
+       SWAP_FUNC(16, s, d, inode_type, struct squashfs_lipc_inode_header);\
+       SWAP_FUNC(16, s, d, mode, struct squashfs_lipc_inode_header);\
+       SWAP_FUNC(16, s, d, uid, struct squashfs_lipc_inode_header);\
+       SWAP_FUNC(16, s, d, guid, struct squashfs_lipc_inode_header);\
+       SWAP_FUNC(32, s, d, mtime, struct squashfs_lipc_inode_header);\
+       SWAP_FUNC(32, s, d, inode_number, struct squashfs_lipc_inode_header);\
+       SWAP_FUNC(32, s, d, nlink, struct squashfs_lipc_inode_header);\
+       SWAP_FUNC(32, s, d, xattr, struct squashfs_lipc_inode_header);\
+}
+
+#define _SQUASHFS_SWAP_DEV_INODE_HEADER(s, d, SWAP_FUNC) {\
+       SWAP_FUNC(16, s, d, inode_type, struct squashfs_dev_inode_header);\
+       SWAP_FUNC(16, s, d, mode, struct squashfs_dev_inode_header);\
+       SWAP_FUNC(16, s, d, uid, struct squashfs_dev_inode_header);\
+       SWAP_FUNC(16, s, d, guid, struct squashfs_dev_inode_header);\
+       SWAP_FUNC(32, s, d, mtime, struct squashfs_dev_inode_header);\
+       SWAP_FUNC(32, s, d, inode_number, struct squashfs_dev_inode_header);\
+       SWAP_FUNC(32, s, d, nlink, struct squashfs_dev_inode_header);\
+       SWAP_FUNC(32, s, d, rdev, struct squashfs_dev_inode_header);\
+}
+
+#define _SQUASHFS_SWAP_LDEV_INODE_HEADER(s, d, SWAP_FUNC) {\
+       SWAP_FUNC(16, s, d, inode_type, struct squashfs_ldev_inode_header);\
+       SWAP_FUNC(16, s, d, mode, struct squashfs_ldev_inode_header);\
+       SWAP_FUNC(16, s, d, uid, struct squashfs_ldev_inode_header);\
+       SWAP_FUNC(16, s, d, guid, struct squashfs_ldev_inode_header);\
+       SWAP_FUNC(32, s, d, mtime, struct squashfs_ldev_inode_header);\
+       SWAP_FUNC(32, s, d, inode_number, struct squashfs_ldev_inode_header);\
+       SWAP_FUNC(32, s, d, nlink, struct squashfs_ldev_inode_header);\
+       SWAP_FUNC(32, s, d, rdev, struct squashfs_ldev_inode_header);\
+       SWAP_FUNC(32, s, d, xattr, struct squashfs_ldev_inode_header);\
+}
+
+#define _SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d, SWAP_FUNC) {\
+       SWAP_FUNC(16, s, d, inode_type, struct squashfs_symlink_inode_header);\
+       SWAP_FUNC(16, s, d, mode, struct squashfs_symlink_inode_header);\
+       SWAP_FUNC(16, s, d, uid, struct squashfs_symlink_inode_header);\
+       SWAP_FUNC(16, s, d, guid, struct squashfs_symlink_inode_header);\
+       SWAP_FUNC(32, s, d, mtime, struct squashfs_symlink_inode_header);\
+       SWAP_FUNC(32, s, d, inode_number, struct squashfs_symlink_inode_header);\
+       SWAP_FUNC(32, s, d, nlink, struct squashfs_symlink_inode_header);\
+       SWAP_FUNC(32, s, d, symlink_size, struct squashfs_symlink_inode_header);\
+}
+
+#define _SQUASHFS_SWAP_REG_INODE_HEADER(s, d, SWAP_FUNC) {\
+       SWAP_FUNC(16, s, d, inode_type, struct squashfs_reg_inode_header);\
+       SWAP_FUNC(16, s, d, mode, struct squashfs_reg_inode_header);\
+       SWAP_FUNC(16, s, d, uid, struct squashfs_reg_inode_header);\
+       SWAP_FUNC(16, s, d, guid, struct squashfs_reg_inode_header);\
+       SWAP_FUNC(32, s, d, mtime, struct squashfs_reg_inode_header);\
+       SWAP_FUNC(32, s, d, inode_number, struct squashfs_reg_inode_header);\
+       SWAP_FUNC(32, s, d, start_block, struct squashfs_reg_inode_header);\
+       SWAP_FUNC(32, s, d, fragment, struct squashfs_reg_inode_header);\
+       SWAP_FUNC(32, s, d, offset, struct squashfs_reg_inode_header);\
+       SWAP_FUNC(32, s, d, file_size, struct squashfs_reg_inode_header);\
+}
+
+#define _SQUASHFS_SWAP_LREG_INODE_HEADER(s, d, SWAP_FUNC) {\
+       SWAP_FUNC(16, s, d, inode_type, struct squashfs_lreg_inode_header);\
+       SWAP_FUNC(16, s, d, mode, struct squashfs_lreg_inode_header);\
+       SWAP_FUNC(16, s, d, uid, struct squashfs_lreg_inode_header);\
+       SWAP_FUNC(16, s, d, guid, struct squashfs_lreg_inode_header);\
+       SWAP_FUNC(32, s, d, mtime, struct squashfs_lreg_inode_header);\
+       SWAP_FUNC(32, s, d, inode_number, struct squashfs_lreg_inode_header);\
+       SWAP_FUNC(64, s, d, start_block, struct squashfs_lreg_inode_header);\
+       SWAP_FUNC(64, s, d, file_size, struct squashfs_lreg_inode_header);\
+       SWAP_FUNC(64, s, d, sparse, struct squashfs_lreg_inode_header);\
+       SWAP_FUNC(32, s, d, nlink, struct squashfs_lreg_inode_header);\
+       SWAP_FUNC(32, s, d, fragment, struct squashfs_lreg_inode_header);\
+       SWAP_FUNC(32, s, d, offset, struct squashfs_lreg_inode_header);\
+       SWAP_FUNC(32, s, d, xattr, struct squashfs_lreg_inode_header);\
+}
+
+#define _SQUASHFS_SWAP_DIR_INODE_HEADER(s, d, SWAP_FUNC) {\
+       SWAP_FUNC(16, s, d, inode_type, struct squashfs_dir_inode_header);\
+       SWAP_FUNC(16, s, d, mode, struct squashfs_dir_inode_header);\
+       SWAP_FUNC(16, s, d, uid, struct squashfs_dir_inode_header);\
+       SWAP_FUNC(16, s, d, guid, struct squashfs_dir_inode_header);\
+       SWAP_FUNC(32, s, d, mtime, struct squashfs_dir_inode_header);\
+       SWAP_FUNC(32, s, d, inode_number, struct squashfs_dir_inode_header);\
+       SWAP_FUNC(32, s, d, start_block, struct squashfs_dir_inode_header);\
+       SWAP_FUNC(32, s, d, nlink, struct squashfs_dir_inode_header);\
+       SWAP_FUNC(16, s, d, file_size, struct squashfs_dir_inode_header);\
+       SWAP_FUNC(16, s, d, offset, struct squashfs_dir_inode_header);\
+       SWAP_FUNC(32, s, d, parent_inode, struct squashfs_dir_inode_header);\
+}
+
+#define _SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d, SWAP_FUNC) {\
+       SWAP_FUNC(16, s, d, inode_type, struct squashfs_ldir_inode_header);\
+       SWAP_FUNC(16, s, d, mode, struct squashfs_ldir_inode_header);\
+       SWAP_FUNC(16, s, d, uid, struct squashfs_ldir_inode_header);\
+       SWAP_FUNC(16, s, d, guid, struct squashfs_ldir_inode_header);\
+       SWAP_FUNC(32, s, d, mtime, struct squashfs_ldir_inode_header);\
+       SWAP_FUNC(32, s, d, inode_number, struct squashfs_ldir_inode_header);\
+       SWAP_FUNC(32, s, d, nlink, struct squashfs_ldir_inode_header);\
+       SWAP_FUNC(32, s, d, file_size, struct squashfs_ldir_inode_header);\
+       SWAP_FUNC(32, s, d, start_block, struct squashfs_ldir_inode_header);\
+       SWAP_FUNC(32, s, d, parent_inode, struct squashfs_ldir_inode_header);\
+       SWAP_FUNC(16, s, d, i_count, struct squashfs_ldir_inode_header);\
+       SWAP_FUNC(16, s, d, offset, struct squashfs_ldir_inode_header);\
+       SWAP_FUNC(32, s, d, xattr, struct squashfs_ldir_inode_header);\
+}
+
+#define _SQUASHFS_SWAP_DIR_ENTRY(s, d, SWAP_FUNC) {\
+       SWAP_FUNC(16, s, d, offset, struct squashfs_dir_entry);\
+       SWAP_FUNC##S(16, s, d, inode_number, struct squashfs_dir_entry);\
+       SWAP_FUNC(16, s, d, type, struct squashfs_dir_entry);\
+       SWAP_FUNC(16, s, d, size, struct squashfs_dir_entry);\
+}
+
+#define _SQUASHFS_SWAP_DIR_HEADER(s, d, SWAP_FUNC) {\
+       SWAP_FUNC(32, s, d, count, struct squashfs_dir_header);\
+       SWAP_FUNC(32, s, d, start_block, struct squashfs_dir_header);\
+       SWAP_FUNC(32, s, d, inode_number, struct squashfs_dir_header);\
+}
+
+#define _SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d, SWAP_FUNC) {\
+       SWAP_FUNC(64, s, d, start_block, struct squashfs_fragment_entry);\
+       SWAP_FUNC(32, s, d, size, struct squashfs_fragment_entry);\
+}
+
+#define _SQUASHFS_SWAP_XATTR_ENTRY(s, d, SWAP_FUNC) {\
+       SWAP_FUNC(16, s, d, type, struct squashfs_xattr_entry);\
+       SWAP_FUNC(16, s, d, size, struct squashfs_xattr_entry);\
+}
+
+#define _SQUASHFS_SWAP_XATTR_VAL(s, d, SWAP_FUNC) {\
+       SWAP_FUNC(32, s, d, vsize, struct squashfs_xattr_val);\
+}
+
+#define _SQUASHFS_SWAP_XATTR_ID(s, d, SWAP_FUNC) {\
+       SWAP_FUNC(64, s, d, xattr, struct squashfs_xattr_id);\
+       SWAP_FUNC(32, s, d, count, struct squashfs_xattr_id);\
+       SWAP_FUNC(32, s, d, size, struct squashfs_xattr_id);\
+}
+
+#define _SQUASHFS_SWAP_XATTR_TABLE(s, d, SWAP_FUNC) {\
+       SWAP_FUNC(64, s, d, xattr_table_start, struct squashfs_xattr_table);\
+       SWAP_FUNC(32, s, d, xattr_ids, struct squashfs_xattr_table);\
+}
+
+/* big endian architecture copy and swap macros */
+#define SQUASHFS_SWAP_SUPER_BLOCK(s, d)        \
+                       _SQUASHFS_SWAP_SUPER_BLOCK(s, d, SWAP_LE)
+#define SQUASHFS_SWAP_DIR_INDEX(s, d) \
+                       _SQUASHFS_SWAP_DIR_INDEX(s, d, SWAP_LE)
+#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d) \
+                       _SQUASHFS_SWAP_BASE_INODE_HEADER(s, d, SWAP_LE)
+#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) \
+                       _SQUASHFS_SWAP_IPC_INODE_HEADER(s, d, SWAP_LE)
+#define SQUASHFS_SWAP_LIPC_INODE_HEADER(s, d) \
+                       _SQUASHFS_SWAP_LIPC_INODE_HEADER(s, d, SWAP_LE)
+#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) \
+                       _SQUASHFS_SWAP_DEV_INODE_HEADER(s, d, SWAP_LE)
+#define SQUASHFS_SWAP_LDEV_INODE_HEADER(s, d) \
+                       _SQUASHFS_SWAP_LDEV_INODE_HEADER(s, d, SWAP_LE)
+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) \
+                       _SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d, SWAP_LE)
+#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) \
+                       _SQUASHFS_SWAP_REG_INODE_HEADER(s, d, SWAP_LE)
+#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) \
+                       _SQUASHFS_SWAP_LREG_INODE_HEADER(s, d, SWAP_LE)
+#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) \
+                       _SQUASHFS_SWAP_DIR_INODE_HEADER(s, d, SWAP_LE)
+#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) \
+                       _SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d, SWAP_LE)
+#define SQUASHFS_SWAP_DIR_ENTRY(s, d) \
+                       _SQUASHFS_SWAP_DIR_ENTRY(s, d, SWAP_LE)
+#define SQUASHFS_SWAP_DIR_HEADER(s, d) \
+                       _SQUASHFS_SWAP_DIR_HEADER(s, d, SWAP_LE)
+#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) \
+                       _SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d, SWAP_LE)
+#define SQUASHFS_SWAP_XATTR_ENTRY(s, d) \
+                        _SQUASHFS_SWAP_XATTR_ENTRY(s, d, SWAP_LE)
+#define SQUASHFS_SWAP_XATTR_VAL(s, d) \
+                       _SQUASHFS_SWAP_XATTR_VAL(s, d, SWAP_LE)
+#define SQUASHFS_SWAP_XATTR_ID(s, d) \
+                        _SQUASHFS_SWAP_XATTR_ID(s, d, SWAP_LE)
+#define SQUASHFS_SWAP_XATTR_TABLE(s, d) \
+                       _SQUASHFS_SWAP_XATTR_TABLE(s, d, SWAP_LE)
+#define SWAP_LE(bits, s, d, field, type) \
+                       SWAP_LE##bits(((void *)(s)) + offsetof(type, field), \
+                               ((void *)(d)) + offsetof(type, field))
+#define SWAP_LES(bits, s, d, field, type) \
+                       SWAP_LE(bits, s, d, field, type)
+#define SQUASHFS_SWAP_INODE_T(s, d) SQUASHFS_SWAP_LONG_LONGS(s, d, 1)
+#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) \
+                       SQUASHFS_SWAP_LONG_LONGS(s, d, n)
+#define SQUASHFS_SWAP_LOOKUP_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
+#define SQUASHFS_SWAP_ID_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
+
+#define SQUASHFS_SWAP_SHORTS(s, d, n) swap_le16_num(s, d, n)
+#define SQUASHFS_SWAP_INTS(s, d, n) swap_le32_num(s, d, n)
+#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) swap_le64_num(s, d, n)
+
+#define SWAP_LE16(s, d)                swap_le16(s, d)
+#define SWAP_LE32(s, d)                swap_le32(s, d)
+#define SWAP_LE64(s, d)                swap_le64(s, d)
+
+/* big endian architecture swap in-place macros */
+#define SQUASHFS_INSWAP_SUPER_BLOCK(s) \
+                       _SQUASHFS_SWAP_SUPER_BLOCK(s, s, INSWAP_LE)
+#define SQUASHFS_INSWAP_DIR_INDEX(s) \
+                       _SQUASHFS_SWAP_DIR_INDEX(s, s, INSWAP_LE)
+#define SQUASHFS_INSWAP_BASE_INODE_HEADER(s) \
+                       _SQUASHFS_SWAP_BASE_INODE_HEADER(s, s, INSWAP_LE)
+#define SQUASHFS_INSWAP_IPC_INODE_HEADER(s) \
+                       _SQUASHFS_SWAP_IPC_INODE_HEADER(s, s, INSWAP_LE)
+#define SQUASHFS_INSWAP_LIPC_INODE_HEADER(s) \
+                       _SQUASHFS_SWAP_LIPC_INODE_HEADER(s, s, INSWAP_LE)
+#define SQUASHFS_INSWAP_DEV_INODE_HEADER(s) \
+                       _SQUASHFS_SWAP_DEV_INODE_HEADER(s, s, INSWAP_LE)
+#define SQUASHFS_INSWAP_LDEV_INODE_HEADER(s) \
+                       _SQUASHFS_SWAP_LDEV_INODE_HEADER(s, s, INSWAP_LE)
+#define SQUASHFS_INSWAP_SYMLINK_INODE_HEADER(s) \
+                       _SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, s, INSWAP_LE)
+#define SQUASHFS_INSWAP_REG_INODE_HEADER(s) \
+                       _SQUASHFS_SWAP_REG_INODE_HEADER(s, s, INSWAP_LE)
+#define SQUASHFS_INSWAP_LREG_INODE_HEADER(s) \
+                       _SQUASHFS_SWAP_LREG_INODE_HEADER(s, s, INSWAP_LE)
+#define SQUASHFS_INSWAP_DIR_INODE_HEADER(s) \
+                       _SQUASHFS_SWAP_DIR_INODE_HEADER(s, s, INSWAP_LE)
+#define SQUASHFS_INSWAP_LDIR_INODE_HEADER(s) \
+                       _SQUASHFS_SWAP_LDIR_INODE_HEADER(s, s, INSWAP_LE)
+#define SQUASHFS_INSWAP_DIR_ENTRY(s) \
+                       _SQUASHFS_SWAP_DIR_ENTRY(s, s, INSWAP_LE)
+#define SQUASHFS_INSWAP_DIR_HEADER(s) \
+                       _SQUASHFS_SWAP_DIR_HEADER(s, s, INSWAP_LE)
+#define SQUASHFS_INSWAP_FRAGMENT_ENTRY(s) \
+                       _SQUASHFS_SWAP_FRAGMENT_ENTRY(s, s, INSWAP_LE)
+#define SQUASHFS_INSWAP_XATTR_ENTRY(s) \
+                        _SQUASHFS_SWAP_XATTR_ENTRY(s, s, INSWAP_LE)
+#define SQUASHFS_INSWAP_XATTR_VAL(s) \
+                       _SQUASHFS_SWAP_XATTR_VAL(s, s, INSWAP_LE)
+#define SQUASHFS_INSWAP_XATTR_ID(s) \
+                        _SQUASHFS_SWAP_XATTR_ID(s, s, INSWAP_LE)
+#define SQUASHFS_INSWAP_XATTR_TABLE(s) \
+                       _SQUASHFS_SWAP_XATTR_TABLE(s, s, INSWAP_LE)
+#define INSWAP_LE(bits, s, d, field, type) \
+                       (s)->field = inswap_le##bits((s)->field)
+#define INSWAP_LES(bits, s, d, field, type) \
+                       (s)->field = (short) inswap_le##bits((unsigned short) \
+                               (s)->field)
+#define SQUASHFS_INSWAP_INODE_T(s) s = inswap_le64(s)
+#define SQUASHFS_INSWAP_FRAGMENT_INDEXES(s, n) inswap_le64_num(s, n)
+#define SQUASHFS_INSWAP_LOOKUP_BLOCKS(s, n) inswap_le64_num(s, n)
+#define SQUASHFS_INSWAP_ID_BLOCKS(s, n) inswap_le64_num(s, n)
+#define SQUASHFS_INSWAP_SHORTS(s, n) inswap_le16_num(s, n)
+#define SQUASHFS_INSWAP_INTS(s, n) inswap_le32_num(s, n)
+#define SQUASHFS_INSWAP_LONG_LONGS(s, n) inswap_le64_num(s, n)
+#else
+/* little endian architecture, just copy */
+#define SQUASHFS_SWAP_SUPER_BLOCK(s, d)        \
+               SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_super_block))
+#define SQUASHFS_SWAP_DIR_INDEX(s, d) \
+               SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_index))
+#define SQUASHFS_SWAP_BASE_INODE_HEADER(s, d) \
+               SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_base_inode_header))
+#define SQUASHFS_SWAP_IPC_INODE_HEADER(s, d) \
+               SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_ipc_inode_header))
+#define SQUASHFS_SWAP_LIPC_INODE_HEADER(s, d) \
+               SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_lipc_inode_header))
+#define SQUASHFS_SWAP_DEV_INODE_HEADER(s, d) \
+               SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dev_inode_header))
+#define SQUASHFS_SWAP_LDEV_INODE_HEADER(s, d) \
+               SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_ldev_inode_header))
+#define SQUASHFS_SWAP_SYMLINK_INODE_HEADER(s, d) \
+               SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_symlink_inode_header))
+#define SQUASHFS_SWAP_REG_INODE_HEADER(s, d) \
+               SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_reg_inode_header))
+#define SQUASHFS_SWAP_LREG_INODE_HEADER(s, d) \
+               SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_lreg_inode_header))
+#define SQUASHFS_SWAP_DIR_INODE_HEADER(s, d) \
+               SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_inode_header))
+#define SQUASHFS_SWAP_LDIR_INODE_HEADER(s, d) \
+               SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_ldir_inode_header))
+#define SQUASHFS_SWAP_DIR_ENTRY(s, d) \
+               SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_entry))
+#define SQUASHFS_SWAP_DIR_HEADER(s, d) \
+               SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_dir_header))
+#define SQUASHFS_SWAP_FRAGMENT_ENTRY(s, d) \
+               SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_fragment_entry))
+#define SQUASHFS_SWAP_XATTR_ENTRY(s, d) \
+               SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_entry))
+#define SQUASHFS_SWAP_XATTR_VAL(s, d) \
+               SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_val))
+#define SQUASHFS_SWAP_XATTR_ID(s, d) \
+               SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_id))
+#define SQUASHFS_SWAP_XATTR_TABLE(s, d) \
+               SQUASHFS_MEMCPY(s, d, sizeof(struct squashfs_xattr_table))
+#define SQUASHFS_SWAP_INODE_T(s, d) SQUASHFS_SWAP_LONG_LONGS(s, d, 1)
+#define SQUASHFS_SWAP_FRAGMENT_INDEXES(s, d, n) \
+                       SQUASHFS_SWAP_LONG_LONGS(s, d, n)
+#define SQUASHFS_SWAP_LOOKUP_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
+#define SQUASHFS_SWAP_ID_BLOCKS(s, d, n) SQUASHFS_SWAP_LONG_LONGS(s, d, n)
+
+#define SQUASHFS_MEMCPY(s, d, n)       memcpy(d, s, n)
+#define SQUASHFS_SWAP_SHORTS(s, d, n)  memcpy(d, s, n * sizeof(short))
+#define SQUASHFS_SWAP_INTS(s, d, n)    memcpy(d, s, n * sizeof(int))
+#define SQUASHFS_SWAP_LONG_LONGS(s, d, n) \
+                                       memcpy(d, s, n * sizeof(long long))
+
+/* little endian architecture, data already in place so do nothing */
+#define SQUASHFS_INSWAP_SUPER_BLOCK(s)
+#define SQUASHFS_INSWAP_DIR_INDEX(s)
+#define SQUASHFS_INSWAP_BASE_INODE_HEADER(s)
+#define SQUASHFS_INSWAP_IPC_INODE_HEADER(s)
+#define SQUASHFS_INSWAP_LIPC_INODE_HEADER(s)
+#define SQUASHFS_INSWAP_DEV_INODE_HEADER(s)
+#define SQUASHFS_INSWAP_LDEV_INODE_HEADER(s)
+#define SQUASHFS_INSWAP_SYMLINK_INODE_HEADER(s)
+#define SQUASHFS_INSWAP_REG_INODE_HEADER(s)
+#define SQUASHFS_INSWAP_LREG_INODE_HEADER(s)
+#define SQUASHFS_INSWAP_DIR_INODE_HEADER(s)
+#define SQUASHFS_INSWAP_LDIR_INODE_HEADER(s)
+#define SQUASHFS_INSWAP_DIR_ENTRY(s)
+#define SQUASHFS_INSWAP_DIR_HEADER(s)
+#define SQUASHFS_INSWAP_FRAGMENT_ENTRY(s)
+#define SQUASHFS_INSWAP_XATTR_ENTRY(s)
+#define SQUASHFS_INSWAP_XATTR_VAL(s)
+#define SQUASHFS_INSWAP_XATTR_ID(s)
+#define SQUASHFS_INSWAP_XATTR_TABLE(s)
+#define SQUASHFS_INSWAP_INODE_T(s)
+#define SQUASHFS_INSWAP_FRAGMENT_INDEXES(s, n)
+#define SQUASHFS_INSWAP_LOOKUP_BLOCKS(s, n)
+#define SQUASHFS_INSWAP_ID_BLOCKS(s, n)
+#define SQUASHFS_INSWAP_SHORTS(s, n)
+#define SQUASHFS_INSWAP_INTS(s, n)
+#define SQUASHFS_INSWAP_LONG_LONGS(s, n)
+#endif
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/swap.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/swap.c
new file mode 100644 (file)
index 0000000..45c8cbc
--- /dev/null
@@ -0,0 +1,123 @@
+/*
+ * Copyright (c) 2009, 2010
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * swap.c
+ */
+
+#ifndef linux
+#define __BYTE_ORDER BYTE_ORDER
+#define __BIG_ENDIAN BIG_ENDIAN
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#else
+#include <endian.h>
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+void swap_le16(void *src, void *dest)
+{
+       unsigned char *s = src;
+       unsigned char *d = dest;
+
+       d[0] = s[1];
+       d[1] = s[0];
+}
+
+
+void swap_le32(void *src, void *dest)
+{
+       unsigned char *s = src;
+       unsigned char *d = dest;
+
+       d[0] = s[3];
+       d[1] = s[2];
+       d[2] = s[1];
+       d[3] = s[0];
+}
+
+
+void swap_le64(void *src, void *dest)
+{
+       unsigned char *s = src;
+       unsigned char *d = dest;
+
+       d[0] = s[7];
+       d[1] = s[6];
+       d[2] = s[5];
+       d[3] = s[4];
+       d[4] = s[3];
+       d[5] = s[2];
+       d[6] = s[1];
+       d[7] = s[0];
+}
+
+
+unsigned short inswap_le16(unsigned short num)
+{
+       return (num >> 8) |
+               ((num & 0xff) << 8);
+}
+
+
+unsigned int inswap_le32(unsigned int num)
+{
+       return (num >> 24) |
+               ((num & 0xff0000) >> 8) |
+               ((num & 0xff00) << 8) |
+               ((num & 0xff) << 24);
+}
+
+
+long long inswap_le64(long long n)
+{
+       unsigned long long num = n;
+
+       return (num >> 56) |
+               ((num & 0xff000000000000LL) >> 40) |
+               ((num & 0xff0000000000LL) >> 24) |
+               ((num & 0xff00000000LL) >> 8) |
+               ((num & 0xff000000) << 8) |
+               ((num & 0xff0000) << 24) |
+               ((num & 0xff00) << 40) |
+               ((num & 0xff) << 56);
+}
+
+
+#define SWAP_LE_NUM(BITS) \
+void swap_le##BITS##_num(void *s, void *d, int n) \
+{\
+       int i;\
+       for(i = 0; i < n; i++, s += BITS / 8, d += BITS / 8)\
+               swap_le##BITS(s, d);\
+}
+
+SWAP_LE_NUM(16)
+SWAP_LE_NUM(32)
+SWAP_LE_NUM(64)
+
+#define INSWAP_LE_NUM(BITS, TYPE) \
+void inswap_le##BITS##_num(TYPE *s, int n) \
+{\
+       int i;\
+       for(i = 0; i < n; i++)\
+               s[i] = inswap_le##BITS(s[i]);\
+}
+
+INSWAP_LE_NUM(16, unsigned short)
+INSWAP_LE_NUM(32, unsigned int)
+INSWAP_LE_NUM(64, long long)
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-1.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-1.c
new file mode 100644 (file)
index 0000000..34eced3
--- /dev/null
@@ -0,0 +1,411 @@
+/*
+ * Unsquash a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2009, 2010, 2011, 2012, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * unsquash-1.c
+ */
+
+#include "unsquashfs.h"
+#include "squashfs_compat.h"
+
+static unsigned int *uid_table, *guid_table;
+static char *inode_table, *directory_table;
+static squashfs_operations ops;
+
+static void read_block_list(unsigned int *block_list, char *block_ptr, int blocks)
+{
+       unsigned short block_size;
+       int i;
+
+       TRACE("read_block_list: blocks %d\n", blocks);
+
+       for(i = 0; i < blocks; i++, block_ptr += 2) {
+               if(swap) {
+                       unsigned short sblock_size;
+                       memcpy(&sblock_size, block_ptr, sizeof(unsigned short));
+                       SQUASHFS_SWAP_SHORTS_3((&block_size), &sblock_size, 1);
+               } else
+                       memcpy(&block_size, block_ptr, sizeof(unsigned short));
+               block_list[i] = SQUASHFS_COMPRESSED_SIZE(block_size) |
+                       (SQUASHFS_COMPRESSED(block_size) ? 0 :
+                       SQUASHFS_COMPRESSED_BIT_BLOCK);
+       }
+}
+
+
+static struct inode *read_inode(unsigned int start_block, unsigned int offset)
+{
+       static union squashfs_inode_header_1 header;
+       long long start = sBlk.s.inode_table_start + start_block;
+       int bytes = lookup_entry(inode_table_hash, start);
+       char *block_ptr = inode_table + bytes + offset;
+       static struct inode i;
+
+       TRACE("read_inode: reading inode [%d:%d]\n", start_block,  offset);
+
+       if(bytes == -1)
+               EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
+                        start); 
+
+       if(swap) {
+               squashfs_base_inode_header_1 sinode;
+               memcpy(&sinode, block_ptr, sizeof(header.base));
+               SQUASHFS_SWAP_BASE_INODE_HEADER_1(&header.base, &sinode,
+                       sizeof(squashfs_base_inode_header_1));
+       } else
+               memcpy(&header.base, block_ptr, sizeof(header.base));
+
+       i.uid = (uid_t) uid_table[(header.base.inode_type - 1) /
+               SQUASHFS_TYPES * 16 + header.base.uid];
+       if(header.base.inode_type == SQUASHFS_IPC_TYPE) {
+               squashfs_ipc_inode_header_1 *inodep = &header.ipc;
+
+               if(swap) {
+                       squashfs_ipc_inode_header_1 sinodep;
+                       memcpy(&sinodep, block_ptr, sizeof(sinodep));
+                       SQUASHFS_SWAP_IPC_INODE_HEADER_1(inodep, &sinodep);
+               } else
+                       memcpy(inodep, block_ptr, sizeof(*inodep));
+
+               if(inodep->type == SQUASHFS_SOCKET_TYPE) {
+                       i.mode = S_IFSOCK | header.base.mode;
+                       i.type = SQUASHFS_SOCKET_TYPE;
+               } else {
+                       i.mode = S_IFIFO | header.base.mode;
+                       i.type = SQUASHFS_FIFO_TYPE;
+               }
+               i.uid = (uid_t) uid_table[inodep->offset * 16 + inodep->uid];
+       } else {
+               i.mode = lookup_type[(header.base.inode_type - 1) %
+                       SQUASHFS_TYPES + 1] | header.base.mode;
+               i.type = (header.base.inode_type - 1) % SQUASHFS_TYPES + 1;
+       }
+
+       i.xattr = SQUASHFS_INVALID_XATTR;
+       i.gid = header.base.guid == 15 ? i.uid :
+               (uid_t) guid_table[header.base.guid];
+       i.time = sBlk.s.mkfs_time;
+       i.inode_number = inode_number ++;
+
+       switch(i.type) {
+               case SQUASHFS_DIR_TYPE: {
+                       squashfs_dir_inode_header_1 *inode = &header.dir;
+
+                       if(swap) {
+                               squashfs_dir_inode_header_1 sinode;
+                               memcpy(&sinode, block_ptr, sizeof(header.dir));
+                               SQUASHFS_SWAP_DIR_INODE_HEADER_1(inode,
+                                       &sinode);
+                       } else
+                       memcpy(inode, block_ptr, sizeof(header.dir));
+
+                       i.data = inode->file_size;
+                       i.offset = inode->offset;
+                       i.start = inode->start_block;
+                       i.time = inode->mtime;
+                       break;
+               }
+               case SQUASHFS_FILE_TYPE: {
+                       squashfs_reg_inode_header_1 *inode = &header.reg;
+
+                       if(swap) {
+                               squashfs_reg_inode_header_1 sinode;
+                               memcpy(&sinode, block_ptr, sizeof(sinode));
+                               SQUASHFS_SWAP_REG_INODE_HEADER_1(inode,
+                                       &sinode);
+                       } else
+                               memcpy(inode, block_ptr, sizeof(*inode));
+
+                       i.data = inode->file_size;
+                       i.time = inode->mtime;
+                       i.blocks = (i.data + sBlk.s.block_size - 1) >>
+                               sBlk.s.block_log;
+                       i.start = inode->start_block;
+                       i.block_ptr = block_ptr + sizeof(*inode);
+                       i.fragment = 0;
+                       i.frag_bytes = 0;
+                       i.offset = 0;
+                       i.sparse = 0;
+                       break;
+               }       
+               case SQUASHFS_SYMLINK_TYPE: {
+                       squashfs_symlink_inode_header_1 *inodep =
+                               &header.symlink;
+
+                       if(swap) {
+                               squashfs_symlink_inode_header_1 sinodep;
+                               memcpy(&sinodep, block_ptr, sizeof(sinodep));
+                               SQUASHFS_SWAP_SYMLINK_INODE_HEADER_1(inodep,
+                                       &sinodep);
+                       } else
+                               memcpy(inodep, block_ptr, sizeof(*inodep));
+
+                       i.symlink = malloc(inodep->symlink_size + 1);
+                       if(i.symlink == NULL)
+                               EXIT_UNSQUASH("read_inode: failed to malloc "
+                                       "symlink data\n");
+                       strncpy(i.symlink, block_ptr +
+                               sizeof(squashfs_symlink_inode_header_1),
+                               inodep->symlink_size);
+                       i.symlink[inodep->symlink_size] = '\0';
+                       i.data = inodep->symlink_size;
+                       break;
+               }
+               case SQUASHFS_BLKDEV_TYPE:
+               case SQUASHFS_CHRDEV_TYPE: {
+                       squashfs_dev_inode_header_1 *inodep = &header.dev;
+
+                       if(swap) {
+                               squashfs_dev_inode_header_1 sinodep;
+                               memcpy(&sinodep, block_ptr, sizeof(sinodep));
+                               SQUASHFS_SWAP_DEV_INODE_HEADER_1(inodep,
+                                       &sinodep);
+                       } else
+                               memcpy(inodep, block_ptr, sizeof(*inodep));
+
+                       i.data = inodep->rdev;
+                       break;
+                       }
+               case SQUASHFS_FIFO_TYPE:
+               case SQUASHFS_SOCKET_TYPE: {
+                       i.data = 0;
+                       break;
+                       }
+               default:
+                       EXIT_UNSQUASH("Unknown inode type %d in "
+                               " read_inode_header_1!\n",
+                               header.base.inode_type);
+       }
+       return &i;
+}
+
+
+static struct dir *squashfs_opendir(unsigned int block_start, unsigned int offset,
+       struct inode **i)
+{
+       squashfs_dir_header_2 dirh;
+       char buffer[sizeof(squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1]
+               __attribute__((aligned));
+       squashfs_dir_entry_2 *dire = (squashfs_dir_entry_2 *) buffer;
+       long long start;
+       int bytes;
+       int dir_count, size;
+       struct dir_ent *new_dir;
+       struct dir *dir;
+
+       TRACE("squashfs_opendir: inode start block %d, offset %d\n",
+               block_start, offset);
+
+       *i = read_inode(block_start, offset);
+
+       dir = malloc(sizeof(struct dir));
+       if(dir == NULL)
+               EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n");
+
+       dir->dir_count = 0;
+       dir->cur_entry = 0;
+       dir->mode = (*i)->mode;
+       dir->uid = (*i)->uid;
+       dir->guid = (*i)->gid;
+       dir->mtime = (*i)->time;
+       dir->xattr = (*i)->xattr;
+       dir->dirs = NULL;
+
+       if ((*i)->data == 0)
+               /*
+                * if the directory is empty, skip the unnecessary
+                * lookup_entry, this fixes the corner case with
+                * completely empty filesystems where lookup_entry correctly
+                * returning -1 is incorrectly treated as an error
+                */
+               return dir;
+               
+       start = sBlk.s.directory_table_start + (*i)->start;
+       bytes = lookup_entry(directory_table_hash, start);
+       if(bytes == -1)
+               EXIT_UNSQUASH("squashfs_opendir: directory block %d not "
+                       "found!\n", block_start);
+
+       bytes += (*i)->offset;
+       size = (*i)->data + bytes;
+
+       while(bytes < size) {                   
+               if(swap) {
+                       squashfs_dir_header_2 sdirh;
+                       memcpy(&sdirh, directory_table + bytes, sizeof(sdirh));
+                       SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
+               } else
+                       memcpy(&dirh, directory_table + bytes, sizeof(dirh));
+       
+               dir_count = dirh.count + 1;
+               TRACE("squashfs_opendir: Read directory header @ byte position "
+                       "%d, %d directory entries\n", bytes, dir_count);
+               bytes += sizeof(dirh);
+
+               /* dir_count should never be larger than SQUASHFS_DIR_COUNT */
+               if(dir_count > SQUASHFS_DIR_COUNT) {
+                       ERROR("File system corrupted: too many entries in directory\n");
+                       goto corrupted;
+               }
+
+               while(dir_count--) {
+                       if(swap) {
+                               squashfs_dir_entry_2 sdire;
+                               memcpy(&sdire, directory_table + bytes,
+                                       sizeof(sdire));
+                               SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
+                       } else
+                               memcpy(dire, directory_table + bytes,
+                                       sizeof(*dire));
+                       bytes += sizeof(*dire);
+
+                       /* size should never be SQUASHFS_NAME_LEN or larger */
+                       if(dire->size >= SQUASHFS_NAME_LEN) {
+                               ERROR("File system corrupted: filename too long\n");
+                               goto corrupted;
+                       }
+
+                       memcpy(dire->name, directory_table + bytes,
+                               dire->size + 1);
+                       dire->name[dire->size + 1] = '\0';
+                       TRACE("squashfs_opendir: directory entry %s, inode "
+                               "%d:%d, type %d\n", dire->name,
+                               dirh.start_block, dire->offset, dire->type);
+                       if((dir->dir_count % DIR_ENT_SIZE) == 0) {
+                               new_dir = realloc(dir->dirs, (dir->dir_count +
+                                       DIR_ENT_SIZE) * sizeof(struct dir_ent));
+                               if(new_dir == NULL)
+                                       EXIT_UNSQUASH("squashfs_opendir: "
+                                               "realloc failed!\n");
+                               dir->dirs = new_dir;
+                       }
+                       strcpy(dir->dirs[dir->dir_count].name, dire->name);
+                       dir->dirs[dir->dir_count].start_block =
+                               dirh.start_block;
+                       dir->dirs[dir->dir_count].offset = dire->offset;
+                       dir->dirs[dir->dir_count].type = dire->type;
+                       dir->dir_count ++;
+                       bytes += dire->size + 1;
+               }
+       }
+
+       return dir;
+
+corrupted:
+       free(dir->dirs);
+       free(dir);
+       return NULL;
+}
+
+
+squashfs_operations *read_filesystem_tables_1()
+{
+       long long table_start;
+
+       /* Read uid and gid lookup tables */
+
+       /* Sanity check super block contents */
+       if(sBlk.no_guids) {
+               if(sBlk.guid_start >= sBlk.s.bytes_used) {
+                       ERROR("read_filesystem_tables: gid start too large in super block\n");
+                       goto corrupted;
+               }
+
+               /* In 1.x filesystems, there should never be more than 15 gids */
+               if(sBlk.no_guids > 15) {
+                       ERROR("read_filesystem_tables: gids too large in super block\n");
+                       goto corrupted;
+               }
+
+               if(read_ids(sBlk.no_guids, sBlk.guid_start, sBlk.s.bytes_used, &guid_table) == FALSE)
+                       goto corrupted;
+
+               table_start = sBlk.guid_start;
+       } else {
+               /* no guids, guid_start should be 0 */
+               if(sBlk.guid_start != 0) {
+                       ERROR("read_filesystem_tables: gid start too large in super block\n");
+                       goto corrupted;
+               }
+
+               table_start = sBlk.s.bytes_used;
+       }
+
+       if(sBlk.uid_start >= table_start) {
+               ERROR("read_filesystem_tables: uid start too large in super block\n");
+               goto corrupted;
+       }
+
+       /* There should be at least one uid */
+       if(sBlk.no_uids == 0) {
+               ERROR("read_filesystem_tables: uid count bad in super block\n");
+               goto corrupted;
+       }
+
+       /* In 1.x filesystems, there should never be more than 48 uids */
+       if(sBlk.no_uids > 48) {
+               ERROR("read_filesystem_tables: uids too large in super block\n");
+               goto corrupted;
+       }
+
+       if(read_ids(sBlk.no_uids, sBlk.uid_start, table_start, &uid_table) == FALSE)
+               goto corrupted;
+
+       table_start = sBlk.uid_start;
+
+       /* Read directory table */
+
+       /* Sanity check super block contents */
+       if(sBlk.s.directory_table_start > table_start) {
+               ERROR("read_filesystem_tables: directory table start too large in super block\n");
+               goto corrupted;
+       }
+
+       directory_table = read_directory_table(sBlk.s.directory_table_start,
+                               table_start);
+       if(directory_table == NULL)
+               goto corrupted;
+
+       /* Read inode table */
+
+       /* Sanity check super block contents */
+       if(sBlk.s.inode_table_start >= sBlk.s.directory_table_start) {
+               ERROR("read_filesystem_tables: inode table start too large in super block\n");
+               goto corrupted;
+       }
+
+       inode_table = read_inode_table(sBlk.s.inode_table_start,
+                               sBlk.s.directory_table_start);
+       if(inode_table == NULL)
+               goto corrupted;
+
+       return &ops;
+
+corrupted:
+       ERROR("File system corruption detected\n");
+       return NULL;
+}
+
+
+static squashfs_operations ops = {
+       .opendir = squashfs_opendir,
+       .read_block_list = read_block_list,
+       .read_inode = read_inode
+};
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-123.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-123.c
new file mode 100644 (file)
index 0000000..8196c80
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * Unsquash a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * unsquash-123.c
+ *
+ * Helper functions used by unsquash-1, unsquash-2 and unsquash-3.
+ */
+
+#include "unsquashfs.h"
+#include "squashfs_compat.h"
+
+int read_ids(int ids, long long start, long long end, unsigned int **id_table)
+{
+       /* Note on overflow limits:
+        * Size of ids is 2^8
+        * Max length is 2^8*4 or 1024
+        */
+       int res;
+       int length = ids * sizeof(unsigned int);
+
+       /*
+        * The size of the index table (length bytes) should match the
+        * table start and end points
+        */
+       if(length != (end - start)) {
+               ERROR("read_ids: Bad inode count in super block\n");
+               return FALSE;
+       }
+
+       TRACE("read_ids: no_ids %d\n", ids);
+
+       *id_table = malloc(length);
+       if(*id_table == NULL) {
+               ERROR("read_ids: failed to allocate uid/gid table\n");
+               return FALSE;
+       }
+
+       if(swap) {
+               unsigned int *sid_table = malloc(length);
+
+               if(sid_table == NULL) {
+                       ERROR("read_ids: failed to allocate uid/gid table\n");
+                       return FALSE;
+               }
+
+               res = read_fs_bytes(fd, start, length, sid_table);
+               if(res == FALSE) {
+                       ERROR("read_ids: failed to read uid/gid table"
+                               "\n");
+                       free(sid_table);
+                       return FALSE;
+               }
+               SQUASHFS_SWAP_INTS_3((*id_table), sid_table, ids);
+               free(sid_table);
+       } else {
+               res = read_fs_bytes(fd, start, length, *id_table);
+               if(res == FALSE) {
+                       ERROR("read_ids: failed to read uid/gid table"
+                               "\n");
+                       return FALSE;
+               }
+       }
+
+       return TRUE;
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-2.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-2.c
new file mode 100644 (file)
index 0000000..4b3d767
--- /dev/null
@@ -0,0 +1,529 @@
+/*
+ * Unsquash a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2009, 2010, 2013, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * unsquash-2.c
+ */
+
+#include "unsquashfs.h"
+#include "squashfs_compat.h"
+
+static squashfs_fragment_entry_2 *fragment_table;
+static unsigned int *uid_table, *guid_table;
+static char *inode_table, *directory_table;
+static squashfs_operations ops;
+
+static void read_block_list(unsigned int *block_list, char *block_ptr, int blocks)
+{
+       TRACE("read_block_list: blocks %d\n", blocks);
+
+       if(swap) {
+               SQUASHFS_SWAP_INTS_3(block_list, block_ptr, blocks);
+       } else
+               memcpy(block_list, block_ptr, blocks * sizeof(unsigned int));
+}
+
+
+static int read_fragment_table(long long *table_start)
+{
+       /*
+        * Note on overflow limits:
+        * Size of SBlk.s.fragments is 2^32 (unsigned int)
+        * Max size of bytes is 2^32*8 or 2^35
+        * Max indexes is (2^32*8)/8K or 2^22
+        * Max length is ((2^32*8)/8K)*4 or 2^24 or 16M
+        */
+       int res, i;
+       long long bytes = SQUASHFS_FRAGMENT_BYTES_2((long long) sBlk.s.fragments);
+       int indexes = SQUASHFS_FRAGMENT_INDEXES_2((long long) sBlk.s.fragments);
+       int length = SQUASHFS_FRAGMENT_INDEX_BYTES_2((long long) sBlk.s.fragments);
+       unsigned int *fragment_table_index;
+
+       /*
+        * The size of the index table (length bytes) should match the
+        * table start and end points
+        */
+       if(length != (*table_start- sBlk.s.fragment_table_start)) {
+               ERROR("read_ids: Bad inode count in super block\n");
+               return FALSE;
+       }
+
+       TRACE("read_fragment_table: %d fragments, reading %d fragment indexes "
+               "from 0x%llx\n", sBlk.s.fragments, indexes,
+               sBlk.s.fragment_table_start);
+
+       fragment_table_index = malloc(length);
+       if(fragment_table_index  == NULL)
+               EXIT_UNSQUASH("read_fragment_table: failed to allocate "
+                       "fragment table index\n");
+
+       fragment_table = malloc(bytes);
+       if(fragment_table == NULL)
+               EXIT_UNSQUASH("read_fragment_table: failed to allocate "
+                       "fragment table\n");
+
+       if(swap) {
+                unsigned int *sfragment_table_index = malloc(length);
+
+               if(sfragment_table_index == NULL)
+                       EXIT_UNSQUASH("read_fragment_table: failed to allocate "
+                               "fragment table index\n");
+
+                res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
+                       length, sfragment_table_index);
+               if(res == FALSE) {
+                       ERROR("read_fragment_table: failed to read fragment "
+                               "table index\n");
+                       free(sfragment_table_index);
+                       goto failed;
+               }
+               SQUASHFS_SWAP_FRAGMENT_INDEXES_2(fragment_table_index,
+                       sfragment_table_index, indexes);
+               free(sfragment_table_index);
+       } else {
+               res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
+                       length, fragment_table_index);
+               if(res == FALSE) {
+                       ERROR("read_fragment_table: failed to read fragment "
+                               "table index\n");
+                       goto failed;
+               }
+       }
+
+       for(i = 0; i < indexes; i++) {
+               int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
+                                       bytes & (SQUASHFS_METADATA_SIZE - 1);
+               int length = read_block(fd, fragment_table_index[i], NULL,
+                       expected, ((char *) fragment_table) + ((long long) i *
+                       SQUASHFS_METADATA_SIZE));
+               TRACE("Read fragment table block %d, from 0x%x, length %d\n", i,
+                       fragment_table_index[i], length);
+               if(length == FALSE) {
+                       ERROR("read_fragment_table: failed to read fragment "
+                               "table block\n");
+                       goto failed;
+               }
+       }
+
+       if(swap) {
+               squashfs_fragment_entry_2 sfragment;
+               for(i = 0; i < sBlk.s.fragments; i++) {
+                       SQUASHFS_SWAP_FRAGMENT_ENTRY_2((&sfragment),
+                               (&fragment_table[i]));
+                       memcpy((char *) &fragment_table[i], (char *) &sfragment,
+                               sizeof(squashfs_fragment_entry_2));
+               }
+       }
+
+       *table_start = fragment_table_index[0];
+       free(fragment_table_index);
+
+       return TRUE;
+
+failed:
+       free(fragment_table_index);
+       return FALSE;
+}
+
+
+static void read_fragment(unsigned int fragment, long long *start_block, int *size)
+{
+       TRACE("read_fragment: reading fragment %d\n", fragment);
+
+       squashfs_fragment_entry_2 *fragment_entry = &fragment_table[fragment];
+       *start_block = fragment_entry->start_block;
+       *size = fragment_entry->size;
+}
+
+
+static struct inode *read_inode(unsigned int start_block, unsigned int offset)
+{
+       static union squashfs_inode_header_2 header;
+       long long start = sBlk.s.inode_table_start + start_block;
+       int bytes = lookup_entry(inode_table_hash, start);
+       char *block_ptr = inode_table + bytes + offset;
+       static struct inode i;
+
+       TRACE("read_inode: reading inode [%d:%d]\n", start_block,  offset);
+
+       if(bytes == -1)
+               EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
+                       start); 
+
+       if(swap) {
+               squashfs_base_inode_header_2 sinode;
+               memcpy(&sinode, block_ptr, sizeof(header.base));
+               SQUASHFS_SWAP_BASE_INODE_HEADER_2(&header.base, &sinode,
+                       sizeof(squashfs_base_inode_header_2));
+       } else
+               memcpy(&header.base, block_ptr, sizeof(header.base));
+
+       i.xattr = SQUASHFS_INVALID_XATTR;
+       i.uid = (uid_t) uid_table[header.base.uid];
+       i.gid = header.base.guid == SQUASHFS_GUIDS ? i.uid :
+               (uid_t) guid_table[header.base.guid];
+       i.mode = lookup_type[header.base.inode_type] | header.base.mode;
+       i.type = header.base.inode_type;
+       i.time = sBlk.s.mkfs_time;
+       i.inode_number = inode_number++;
+
+       switch(header.base.inode_type) {
+               case SQUASHFS_DIR_TYPE: {
+                       squashfs_dir_inode_header_2 *inode = &header.dir;
+
+                       if(swap) {
+                               squashfs_dir_inode_header_2 sinode;
+                               memcpy(&sinode, block_ptr, sizeof(header.dir));
+                               SQUASHFS_SWAP_DIR_INODE_HEADER_2(&header.dir,
+                                       &sinode);
+                       } else
+                               memcpy(&header.dir, block_ptr,
+                                       sizeof(header.dir));
+
+                       i.data = inode->file_size;
+                       i.offset = inode->offset;
+                       i.start = inode->start_block;
+                       i.time = inode->mtime;
+                       break;
+               }
+               case SQUASHFS_LDIR_TYPE: {
+                       squashfs_ldir_inode_header_2 *inode = &header.ldir;
+
+                       if(swap) {
+                               squashfs_ldir_inode_header_2 sinode;
+                               memcpy(&sinode, block_ptr, sizeof(header.ldir));
+                               SQUASHFS_SWAP_LDIR_INODE_HEADER_2(&header.ldir,
+                                       &sinode);
+                       } else
+                               memcpy(&header.ldir, block_ptr,
+                                       sizeof(header.ldir));
+
+                       i.data = inode->file_size;
+                       i.offset = inode->offset;
+                       i.start = inode->start_block;
+                       i.time = inode->mtime;
+                       break;
+               }
+               case SQUASHFS_FILE_TYPE: {
+                       squashfs_reg_inode_header_2 *inode = &header.reg;
+
+                       if(swap) {
+                               squashfs_reg_inode_header_2 sinode;
+                               memcpy(&sinode, block_ptr, sizeof(sinode));
+                               SQUASHFS_SWAP_REG_INODE_HEADER_2(inode,
+                                       &sinode);
+                       } else
+                               memcpy(inode, block_ptr, sizeof(*inode));
+
+                       i.data = inode->file_size;
+                       i.time = inode->mtime;
+                       i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
+                               ?  0 : inode->file_size % sBlk.s.block_size;
+                       i.fragment = inode->fragment;
+                       i.offset = inode->offset;
+                       i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
+                               (i.data + sBlk.s.block_size - 1) >>
+                               sBlk.s.block_log : i.data >>
+                               sBlk.s.block_log;
+                       i.start = inode->start_block;
+                       i.sparse = 0;
+                       i.block_ptr = block_ptr + sizeof(*inode);
+                       break;
+               }       
+               case SQUASHFS_SYMLINK_TYPE: {
+                       squashfs_symlink_inode_header_2 *inodep =
+                               &header.symlink;
+
+                       if(swap) {
+                               squashfs_symlink_inode_header_2 sinodep;
+                               memcpy(&sinodep, block_ptr, sizeof(sinodep));
+                               SQUASHFS_SWAP_SYMLINK_INODE_HEADER_2(inodep,
+                                       &sinodep);
+                       } else
+                               memcpy(inodep, block_ptr, sizeof(*inodep));
+
+                       i.symlink = malloc(inodep->symlink_size + 1);
+                       if(i.symlink == NULL)
+                               EXIT_UNSQUASH("read_inode: failed to malloc "
+                                       "symlink data\n");
+                       strncpy(i.symlink, block_ptr +
+                               sizeof(squashfs_symlink_inode_header_2),
+                               inodep->symlink_size);
+                       i.symlink[inodep->symlink_size] = '\0';
+                       i.data = inodep->symlink_size;
+                       break;
+               }
+               case SQUASHFS_BLKDEV_TYPE:
+               case SQUASHFS_CHRDEV_TYPE: {
+                       squashfs_dev_inode_header_2 *inodep = &header.dev;
+
+                       if(swap) {
+                               squashfs_dev_inode_header_2 sinodep;
+                               memcpy(&sinodep, block_ptr, sizeof(sinodep));
+                               SQUASHFS_SWAP_DEV_INODE_HEADER_2(inodep,
+                                       &sinodep);
+                       } else
+                               memcpy(inodep, block_ptr, sizeof(*inodep));
+
+                       i.data = inodep->rdev;
+                       break;
+                       }
+               case SQUASHFS_FIFO_TYPE:
+               case SQUASHFS_SOCKET_TYPE:
+                       i.data = 0;
+                       break;
+               default:
+                       EXIT_UNSQUASH("Unknown inode type %d in "
+                               "read_inode_header_2!\n",
+                               header.base.inode_type);
+       }
+       return &i;
+}
+
+
+static struct dir *squashfs_opendir(unsigned int block_start, unsigned int offset,
+       struct inode **i)
+{
+       squashfs_dir_header_2 dirh;
+       char buffer[sizeof(squashfs_dir_entry_2) + SQUASHFS_NAME_LEN + 1]
+               __attribute__((aligned));
+       squashfs_dir_entry_2 *dire = (squashfs_dir_entry_2 *) buffer;
+       long long start;
+       int bytes;
+       int dir_count, size;
+       struct dir_ent *new_dir;
+       struct dir *dir;
+
+       TRACE("squashfs_opendir: inode start block %d, offset %d\n",
+               block_start, offset);
+
+       *i = read_inode(block_start, offset);
+
+       dir = malloc(sizeof(struct dir));
+       if(dir == NULL)
+               EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n");
+
+       dir->dir_count = 0;
+       dir->cur_entry = 0;
+       dir->mode = (*i)->mode;
+       dir->uid = (*i)->uid;
+       dir->guid = (*i)->gid;
+       dir->mtime = (*i)->time;
+       dir->xattr = (*i)->xattr;
+       dir->dirs = NULL;
+
+       if ((*i)->data == 0)
+               /*
+                * if the directory is empty, skip the unnecessary
+                * lookup_entry, this fixes the corner case with
+                * completely empty filesystems where lookup_entry correctly
+                * returning -1 is incorrectly treated as an error
+                */
+               return dir;
+
+       start = sBlk.s.directory_table_start + (*i)->start;
+       bytes = lookup_entry(directory_table_hash, start);
+       if(bytes == -1)
+               EXIT_UNSQUASH("squashfs_opendir: directory block %d not "
+                       "found!\n", block_start);
+
+       bytes += (*i)->offset;
+       size = (*i)->data + bytes;
+
+       while(bytes < size) {
+               if(swap) {
+                       squashfs_dir_header_2 sdirh;
+                       memcpy(&sdirh, directory_table + bytes, sizeof(sdirh));
+                       SQUASHFS_SWAP_DIR_HEADER_2(&dirh, &sdirh);
+               } else
+                       memcpy(&dirh, directory_table + bytes, sizeof(dirh));
+
+               dir_count = dirh.count + 1;
+               TRACE("squashfs_opendir: Read directory header @ byte position "
+                       "%d, %d directory entries\n", bytes, dir_count);
+               bytes += sizeof(dirh);
+
+               /* dir_count should never be larger than SQUASHFS_DIR_COUNT */
+               if(dir_count > SQUASHFS_DIR_COUNT) {
+                       ERROR("File system corrupted: too many entries in directory\n");
+                       goto corrupted;
+               }
+
+               while(dir_count--) {
+                       if(swap) {
+                               squashfs_dir_entry_2 sdire;
+                               memcpy(&sdire, directory_table + bytes,
+                                       sizeof(sdire));
+                               SQUASHFS_SWAP_DIR_ENTRY_2(dire, &sdire);
+                       } else
+                               memcpy(dire, directory_table + bytes,
+                                       sizeof(*dire));
+                       bytes += sizeof(*dire);
+
+                       /* size should never be SQUASHFS_NAME_LEN or larger */
+                       if(dire->size >= SQUASHFS_NAME_LEN) {
+                               ERROR("File system corrupted: filename too long\n");
+                               goto corrupted;
+                       }
+
+                       memcpy(dire->name, directory_table + bytes,
+                               dire->size + 1);
+                       dire->name[dire->size + 1] = '\0';
+                       TRACE("squashfs_opendir: directory entry %s, inode "
+                               "%d:%d, type %d\n", dire->name,
+                               dirh.start_block, dire->offset, dire->type);
+                       if((dir->dir_count % DIR_ENT_SIZE) == 0) {
+                               new_dir = realloc(dir->dirs, (dir->dir_count +
+                                       DIR_ENT_SIZE) * sizeof(struct dir_ent));
+                               if(new_dir == NULL)
+                                       EXIT_UNSQUASH("squashfs_opendir: "
+                                               "realloc failed!\n");
+                               dir->dirs = new_dir;
+                       }
+                       strcpy(dir->dirs[dir->dir_count].name, dire->name);
+                       dir->dirs[dir->dir_count].start_block =
+                               dirh.start_block;
+                       dir->dirs[dir->dir_count].offset = dire->offset;
+                       dir->dirs[dir->dir_count].type = dire->type;
+                       dir->dir_count ++;
+                       bytes += dire->size + 1;
+               }
+       }
+
+       return dir;
+
+corrupted:
+       free(dir->dirs);
+       free(dir);
+       return NULL;
+}
+
+
+squashfs_operations *read_filesystem_tables_2()
+{
+       long long table_start;
+
+       /* Read uid and gid lookup tables */
+
+       /* Sanity check super block contents */
+       if(sBlk.no_guids) {
+               if(sBlk.guid_start >= sBlk.s.bytes_used) {
+                       ERROR("read_filesystem_tables: gid start too large in super block\n");
+                       goto corrupted;
+               }
+
+               if(read_ids(sBlk.no_guids, sBlk.guid_start, sBlk.s.bytes_used, &guid_table) == FALSE)
+                       goto corrupted;
+
+               table_start = sBlk.guid_start;
+       } else {
+               /* no guids, guid_start should be 0 */
+               if(sBlk.guid_start != 0) {
+                       ERROR("read_filesystem_tables: gid start too large in super block\n");
+                       goto corrupted;
+               }
+
+               table_start = sBlk.s.bytes_used;
+       }
+
+       if(sBlk.uid_start >= table_start) {
+               ERROR("read_filesystem_tables: uid start too large in super block\n");
+               goto corrupted;
+       }
+
+       /* There should be at least one uid */
+       if(sBlk.no_uids == 0) {
+               ERROR("read_filesystem_tables: uid count bad in super block\n");
+               goto corrupted;
+       }
+
+       if(read_ids(sBlk.no_uids, sBlk.uid_start, table_start, &uid_table) == FALSE)
+               goto corrupted;
+
+       table_start = sBlk.uid_start;
+
+       /* Read fragment table */
+       if(sBlk.s.fragments != 0) {
+
+               /* Sanity check super block contents */
+               if(sBlk.s.fragment_table_start >= table_start) {
+                       ERROR("read_filesystem_tables: fragment table start too large in super block\n");
+                       goto corrupted;
+               }
+
+               /* The number of fragments should not exceed the number of inodes */
+               if(sBlk.s.fragments > sBlk.s.inodes) {
+                       ERROR("read_filesystem_tables: Bad fragment count in super block\n");
+                       goto corrupted;
+               }
+
+               if(read_fragment_table(&table_start) == FALSE)
+                       goto corrupted;
+       } else {
+               /*
+                * Sanity check super block contents - with 0 fragments,
+                * the fragment table should be empty
+                */
+               if(sBlk.s.fragment_table_start != table_start) {
+                       ERROR("read_filesystem_tables: fragment table start invalid in super block\n");
+                       goto corrupted;
+               }
+       }
+
+       /* Read directory table */
+
+       /* Sanity check super block contents */
+       if(sBlk.s.directory_table_start > table_start) {
+               ERROR("read_filesystem_tables: directory table start too large in super block\n");
+               goto corrupted;
+       }
+
+       directory_table = read_directory_table(sBlk.s.directory_table_start,
+                               table_start);
+       if(directory_table == NULL)
+               goto corrupted;
+
+       /* Read inode table */
+
+       /* Sanity check super block contents */
+       if(sBlk.s.inode_table_start >= sBlk.s.directory_table_start) {
+               ERROR("read_filesystem_tables: inode table start too large in super block\n");
+               goto corrupted;
+       }
+
+       inode_table = read_inode_table(sBlk.s.inode_table_start,
+                               sBlk.s.directory_table_start);
+       if(inode_table == NULL)
+               goto corrupted;
+
+       return &ops;
+
+corrupted:
+       ERROR("File system corruption detected\n");
+       return NULL;
+}
+
+
+static squashfs_operations ops = {
+       .opendir = squashfs_opendir,
+       .read_fragment = read_fragment,
+       .read_block_list = read_block_list,
+       .read_inode = read_inode
+};
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-3.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-3.c
new file mode 100644 (file)
index 0000000..02c31fc
--- /dev/null
@@ -0,0 +1,632 @@
+/*
+ * Unsquash a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * unsquash-3.c
+ */
+
+#include "unsquashfs.h"
+#include "squashfs_compat.h"
+
+static squashfs_fragment_entry_3 *fragment_table;
+static unsigned int *uid_table, *guid_table;
+static char *inode_table, *directory_table;
+static squashfs_operations ops;
+
+static long long *salloc_index_table(int indexes)
+{
+       static long long *alloc_table = NULL;
+       static int alloc_size = 0;
+       int length = indexes * sizeof(long long);
+
+       if(alloc_size < length || length == 0) {
+               long long *table = realloc(alloc_table, length);
+
+               if(table == NULL && length !=0 )
+                       EXIT_UNSQUASH("alloc_index_table: failed to allocate "
+                               "index table\n");
+
+               alloc_table = table;
+               alloc_size = length;
+       }
+
+       return alloc_table;
+}
+
+
+static void read_block_list(unsigned int *block_list, char *block_ptr, int blocks)
+{
+       TRACE("read_block_list: blocks %d\n", blocks);
+
+       if(swap) {
+               SQUASHFS_SWAP_INTS_3(block_list, block_ptr, blocks);
+       } else
+               memcpy(block_list, block_ptr, blocks * sizeof(unsigned int));
+}
+
+
+static int read_fragment_table(long long *table_start)
+{
+       /*
+        * Note on overflow limits:
+        * Size of SBlk.s.fragments is 2^32 (unsigned int)
+        * Max size of bytes is 2^32*16 or 2^36
+        * Max indexes is (2^32*16)/8K or 2^23
+        * Max length is ((2^32*16)/8K)*8 or 2^26 or 64M
+        */
+       int res, i;
+       long long bytes = SQUASHFS_FRAGMENT_BYTES_3((long long) sBlk.s.fragments);
+       int indexes = SQUASHFS_FRAGMENT_INDEXES_3((long long) sBlk.s.fragments);
+       int length = SQUASHFS_FRAGMENT_INDEX_BYTES_3((long long) sBlk.s.fragments);
+       long long *fragment_table_index;
+
+       /*
+        * The size of the index table (length bytes) should match the
+        * table start and end points
+        */
+       if(length != (*table_start - sBlk.s.fragment_table_start)) {
+               ERROR("read_fragment_table: Bad fragment count in super block\n");
+               return FALSE;
+       }
+
+       TRACE("read_fragment_table: %d fragments, reading %d fragment indexes "
+               "from 0x%llx\n", sBlk.s.fragments, indexes,
+               sBlk.s.fragment_table_start);
+
+       fragment_table_index = alloc_index_table(indexes);
+       fragment_table = malloc(bytes);
+       if(fragment_table == NULL)
+               EXIT_UNSQUASH("read_fragment_table: failed to allocate "
+                       "fragment table\n");
+
+       if(swap) {
+               long long *sfragment_table_index = salloc_index_table(indexes);
+
+               res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
+                       length, sfragment_table_index);
+               if(res == FALSE) {
+                       ERROR("read_fragment_table: failed to read fragment "
+                               "table index\n");       
+                       return FALSE;
+               }
+               SQUASHFS_SWAP_FRAGMENT_INDEXES_3(fragment_table_index,
+                       sfragment_table_index, indexes);
+       } else {
+               res = read_fs_bytes(fd, sBlk.s.fragment_table_start,
+                       length, fragment_table_index);
+               if(res == FALSE) {
+                       ERROR("read_fragment_table: failed to read fragment "
+                               "table index\n");       
+                       return FALSE;
+               }
+       }
+
+       for(i = 0; i < indexes; i++) {
+               int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
+                                       bytes & (SQUASHFS_METADATA_SIZE - 1);
+               int length = read_block(fd, fragment_table_index[i], NULL,
+                       expected, ((char *) fragment_table) + ((long long) i *
+                       SQUASHFS_METADATA_SIZE));
+               TRACE("Read fragment table block %d, from 0x%llx, length %d\n",
+                       i, fragment_table_index[i], length);
+               if(length == FALSE) {
+                       ERROR("read_fragment_table: failed to read fragment "
+                               "table block\n");       
+                       return FALSE;
+               }
+       }
+
+       if(swap) {
+               squashfs_fragment_entry_3 sfragment;
+               for(i = 0; i < sBlk.s.fragments; i++) {
+                       SQUASHFS_SWAP_FRAGMENT_ENTRY_3((&sfragment),
+                               (&fragment_table[i]));
+                       memcpy((char *) &fragment_table[i], (char *) &sfragment,
+                               sizeof(squashfs_fragment_entry_3));
+               }
+       }
+
+       *table_start = fragment_table_index[0];
+       return TRUE;
+}
+
+
+static void read_fragment(unsigned int fragment, long long *start_block, int *size)
+{
+       TRACE("read_fragment: reading fragment %d\n", fragment);
+
+       squashfs_fragment_entry_3 *fragment_entry = &fragment_table[fragment];
+       *start_block = fragment_entry->start_block;
+       *size = fragment_entry->size;
+}
+
+
+static struct inode *read_inode(unsigned int start_block, unsigned int offset)
+{
+       static union squashfs_inode_header_3 header;
+       long long start = sBlk.s.inode_table_start + start_block;
+       int bytes = lookup_entry(inode_table_hash, start);
+       char *block_ptr = inode_table + bytes + offset;
+       static struct inode i;
+
+       TRACE("read_inode: reading inode [%d:%d]\n", start_block,  offset);
+
+       if(bytes == -1)
+               EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
+                       start); 
+
+       if(swap) {
+               squashfs_base_inode_header_3 sinode;
+               memcpy(&sinode, block_ptr, sizeof(header.base));
+               SQUASHFS_SWAP_BASE_INODE_HEADER_3(&header.base, &sinode,
+                       sizeof(squashfs_base_inode_header_3));
+       } else
+               memcpy(&header.base, block_ptr, sizeof(header.base));
+
+       i.xattr = SQUASHFS_INVALID_XATTR;
+       i.uid = (uid_t) uid_table[header.base.uid];
+       i.gid = header.base.guid == SQUASHFS_GUIDS ? i.uid :
+               (uid_t) guid_table[header.base.guid];
+       i.mode = lookup_type[header.base.inode_type] | header.base.mode;
+       i.type = header.base.inode_type;
+       i.time = header.base.mtime;
+       i.inode_number = header.base.inode_number;
+
+       switch(header.base.inode_type) {
+               case SQUASHFS_DIR_TYPE: {
+                       squashfs_dir_inode_header_3 *inode = &header.dir;
+
+                       if(swap) {
+                               squashfs_dir_inode_header_3 sinode;
+                               memcpy(&sinode, block_ptr, sizeof(header.dir));
+                               SQUASHFS_SWAP_DIR_INODE_HEADER_3(&header.dir,
+                                       &sinode);
+                       } else
+                               memcpy(&header.dir, block_ptr,
+                                       sizeof(header.dir));
+
+                       i.data = inode->file_size;
+                       i.offset = inode->offset;
+                       i.start = inode->start_block;
+                       break;
+               }
+               case SQUASHFS_LDIR_TYPE: {
+                       squashfs_ldir_inode_header_3 *inode = &header.ldir;
+
+                       if(swap) {
+                               squashfs_ldir_inode_header_3 sinode;
+                               memcpy(&sinode, block_ptr, sizeof(header.ldir));
+                               SQUASHFS_SWAP_LDIR_INODE_HEADER_3(&header.ldir,
+                                       &sinode);
+                       } else
+                               memcpy(&header.ldir, block_ptr,
+                                       sizeof(header.ldir));
+
+                       i.data = inode->file_size;
+                       i.offset = inode->offset;
+                       i.start = inode->start_block;
+                       break;
+               }
+               case SQUASHFS_FILE_TYPE: {
+                       squashfs_reg_inode_header_3 *inode = &header.reg;
+
+                       if(swap) {
+                               squashfs_reg_inode_header_3 sinode;
+                               memcpy(&sinode, block_ptr, sizeof(sinode));
+                               SQUASHFS_SWAP_REG_INODE_HEADER_3(inode,
+                                       &sinode);
+                       } else
+                               memcpy(inode, block_ptr, sizeof(*inode));
+
+                       i.data = inode->file_size;
+                       i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
+                               ?  0 : inode->file_size % sBlk.s.block_size;
+                       i.fragment = inode->fragment;
+                       i.offset = inode->offset;
+                       i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
+                               (i.data + sBlk.s.block_size - 1) >>
+                               sBlk.s.block_log :
+                               i.data >> sBlk.s.block_log;
+                       i.start = inode->start_block;
+                       i.sparse = 1;
+                       i.block_ptr = block_ptr + sizeof(*inode);
+                       break;
+               }       
+               case SQUASHFS_LREG_TYPE: {
+                       squashfs_lreg_inode_header_3 *inode = &header.lreg;
+
+                       if(swap) {
+                               squashfs_lreg_inode_header_3 sinode;
+                               memcpy(&sinode, block_ptr, sizeof(sinode));
+                               SQUASHFS_SWAP_LREG_INODE_HEADER_3(inode,
+                                       &sinode);
+                       } else
+                               memcpy(inode, block_ptr, sizeof(*inode));
+
+                       i.data = inode->file_size;
+                       i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
+                               ?  0 : inode->file_size % sBlk.s.block_size;
+                       i.fragment = inode->fragment;
+                       i.offset = inode->offset;
+                       i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
+                               (inode->file_size + sBlk.s.block_size - 1) >>
+                               sBlk.s.block_log :
+                               inode->file_size >> sBlk.s.block_log;
+                       i.start = inode->start_block;
+                       i.sparse = 1;
+                       i.block_ptr = block_ptr + sizeof(*inode);
+                       break;
+               }       
+               case SQUASHFS_SYMLINK_TYPE: {
+                       squashfs_symlink_inode_header_3 *inodep =
+                               &header.symlink;
+
+                       if(swap) {
+                               squashfs_symlink_inode_header_3 sinodep;
+                               memcpy(&sinodep, block_ptr, sizeof(sinodep));
+                               SQUASHFS_SWAP_SYMLINK_INODE_HEADER_3(inodep,
+                                       &sinodep);
+                       } else
+                               memcpy(inodep, block_ptr, sizeof(*inodep));
+
+                       i.symlink = malloc(inodep->symlink_size + 1);
+                       if(i.symlink == NULL)
+                               EXIT_UNSQUASH("read_inode: failed to malloc "
+                                       "symlink data\n");
+                       strncpy(i.symlink, block_ptr +
+                               sizeof(squashfs_symlink_inode_header_3),
+                               inodep->symlink_size);
+                       i.symlink[inodep->symlink_size] = '\0';
+                       i.data = inodep->symlink_size;
+                       break;
+               }
+               case SQUASHFS_BLKDEV_TYPE:
+               case SQUASHFS_CHRDEV_TYPE: {
+                       squashfs_dev_inode_header_3 *inodep = &header.dev;
+
+                       if(swap) {
+                               squashfs_dev_inode_header_3 sinodep;
+                               memcpy(&sinodep, block_ptr, sizeof(sinodep));
+                               SQUASHFS_SWAP_DEV_INODE_HEADER_3(inodep,
+                                       &sinodep);
+                       } else
+                               memcpy(inodep, block_ptr, sizeof(*inodep));
+
+                       i.data = inodep->rdev;
+                       break;
+                       }
+               case SQUASHFS_FIFO_TYPE:
+               case SQUASHFS_SOCKET_TYPE:
+                       i.data = 0;
+                       break;
+               default:
+                       EXIT_UNSQUASH("Unknown inode type %d in read_inode!\n",
+                               header.base.inode_type);
+       }
+       return &i;
+}
+
+
+static struct dir *squashfs_opendir(unsigned int block_start, unsigned int offset,
+       struct inode **i)
+{
+       squashfs_dir_header_3 dirh;
+       char buffer[sizeof(squashfs_dir_entry_3) + SQUASHFS_NAME_LEN + 1]
+               __attribute__((aligned));
+       squashfs_dir_entry_3 *dire = (squashfs_dir_entry_3 *) buffer;
+       long long start;
+       int bytes;
+       int dir_count, size;
+       struct dir_ent *new_dir;
+       struct dir *dir;
+
+       TRACE("squashfs_opendir: inode start block %d, offset %d\n",
+               block_start, offset);
+
+       *i = read_inode(block_start, offset);
+
+       dir = malloc(sizeof(struct dir));
+       if(dir == NULL)
+               EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n");
+
+       dir->dir_count = 0;
+       dir->cur_entry = 0;
+       dir->mode = (*i)->mode;
+       dir->uid = (*i)->uid;
+       dir->guid = (*i)->gid;
+       dir->mtime = (*i)->time;
+       dir->xattr = (*i)->xattr;
+       dir->dirs = NULL;
+
+       if ((*i)->data == 3)
+               /*
+                * if the directory is empty, skip the unnecessary
+                * lookup_entry, this fixes the corner case with
+                * completely empty filesystems where lookup_entry correctly
+                * returning -1 is incorrectly treated as an error
+                */
+               return dir;
+
+       start = sBlk.s.directory_table_start + (*i)->start;
+       bytes = lookup_entry(directory_table_hash, start);
+
+       if(bytes == -1)
+               EXIT_UNSQUASH("squashfs_opendir: directory block %d not "
+                       "found!\n", block_start);
+
+       bytes += (*i)->offset;
+       size = (*i)->data + bytes - 3;
+
+       while(bytes < size) {                   
+               if(swap) {
+                       squashfs_dir_header_3 sdirh;
+                       memcpy(&sdirh, directory_table + bytes, sizeof(sdirh));
+                       SQUASHFS_SWAP_DIR_HEADER_3(&dirh, &sdirh);
+               } else
+                       memcpy(&dirh, directory_table + bytes, sizeof(dirh));
+       
+               dir_count = dirh.count + 1;
+               TRACE("squashfs_opendir: Read directory header @ byte position "
+                       "%d, %d directory entries\n", bytes, dir_count);
+               bytes += sizeof(dirh);
+
+               /* dir_count should never be larger than SQUASHFS_DIR_COUNT */
+               if(dir_count > SQUASHFS_DIR_COUNT) {
+                       ERROR("File system corrupted: too many entries in directory\n");
+                       goto corrupted;
+               }
+
+               while(dir_count--) {
+                       if(swap) {
+                               squashfs_dir_entry_3 sdire;
+                               memcpy(&sdire, directory_table + bytes,
+                                       sizeof(sdire));
+                               SQUASHFS_SWAP_DIR_ENTRY_3(dire, &sdire);
+                       } else
+                               memcpy(dire, directory_table + bytes,
+                                       sizeof(*dire));
+                       bytes += sizeof(*dire);
+
+                       /* size should never be SQUASHFS_NAME_LEN or larger */
+                       if(dire->size >= SQUASHFS_NAME_LEN) {
+                               ERROR("File system corrupted: filename too long\n");
+                               goto corrupted;
+                       }
+
+                       memcpy(dire->name, directory_table + bytes,
+                               dire->size + 1);
+                       dire->name[dire->size + 1] = '\0';
+                       TRACE("squashfs_opendir: directory entry %s, inode "
+                               "%d:%d, type %d\n", dire->name,
+                               dirh.start_block, dire->offset, dire->type);
+                       if((dir->dir_count % DIR_ENT_SIZE) == 0) {
+                               new_dir = realloc(dir->dirs, (dir->dir_count +
+                                       DIR_ENT_SIZE) * sizeof(struct dir_ent));
+                               if(new_dir == NULL)
+                                       EXIT_UNSQUASH("squashfs_opendir: "
+                                               "realloc failed!\n");
+                               dir->dirs = new_dir;
+                       }
+                       strcpy(dir->dirs[dir->dir_count].name, dire->name);
+                       dir->dirs[dir->dir_count].start_block =
+                               dirh.start_block;
+                       dir->dirs[dir->dir_count].offset = dire->offset;
+                       dir->dirs[dir->dir_count].type = dire->type;
+                       dir->dir_count ++;
+                       bytes += dire->size + 1;
+               }
+       }
+
+       return dir;
+
+corrupted:
+       free(dir->dirs);
+       free(dir);
+       return NULL;
+}
+
+
+static int parse_exports_table(long long *table_start)
+{
+       /*
+        * Note on overflow limits:
+        * Size of SBlk.s.inodes is 2^32 (unsigned int)
+        * Max indexes is (2^32*8)/8K or 2^22
+        * Max length is ((2^32*8)/8K)*8 or 2^25
+        */
+       int res;
+       int indexes = SQUASHFS_LOOKUP_BLOCKS_3((long long) sBlk.s.inodes);
+       int length = SQUASHFS_LOOKUP_BLOCK_BYTES_3((long long) sBlk.s.inodes);
+       long long *export_index_table;
+
+       /*
+        * The size of the index table (length bytes) should match the
+        * table start and end points
+        */
+       if(length != (*table_start - sBlk.s.lookup_table_start)) {
+               ERROR("parse_exports_table: Bad inode count in super block\n");
+               return FALSE;
+       }
+
+       export_index_table = alloc_index_table(indexes);
+
+       if(swap) {
+               long long *sexport_index_table = salloc_index_table(indexes);
+
+               res = read_fs_bytes(fd, sBlk.s.lookup_table_start,
+                       length, sexport_index_table);
+               if(res == FALSE) {
+                       ERROR("parse_exorts_table: failed to read export "
+                               "index table\n");
+                       return FALSE;
+               }
+               SQUASHFS_SWAP_LOOKUP_BLOCKS_3(export_index_table,
+                       sexport_index_table, indexes);
+       } else {
+               res = read_fs_bytes(fd, sBlk.s.lookup_table_start, length,
+                                                       export_index_table);
+               if(res == FALSE) {
+                       ERROR("parse_exorts_table: failed to read export "
+                               "index table\n");
+                       return FALSE;
+               }
+       }
+
+       /*
+        * export_index_table[0] stores the start of the compressed export blocks.
+        * This by definition is also the end of the previous filesystem
+        * table - the fragment table.
+        */
+       *table_start = export_index_table[0];
+
+       return TRUE;
+}
+
+
+squashfs_operations *read_filesystem_tables_3()
+{
+       long long table_start;
+
+       /* Read uid and gid lookup tables */
+
+       /* Sanity check super block contents */
+       if(sBlk.no_guids) {
+               if(sBlk.guid_start >= sBlk.s.bytes_used) {
+                       ERROR("read_filesystem_tables: gid start too large in super block\n");
+                       goto corrupted;
+               }
+
+               if(read_ids(sBlk.no_guids, sBlk.guid_start, sBlk.s.bytes_used, &guid_table) == FALSE)
+                       goto corrupted;
+
+               table_start = sBlk.guid_start;
+       } else {
+               /* no guids, guid_start should be 0 */
+               if(sBlk.guid_start != 0) {
+                       ERROR("read_filesystem_tables: gid start too large in super block\n");
+                       goto corrupted;
+               }
+
+               table_start = sBlk.s.bytes_used;
+       }
+
+       if(sBlk.uid_start >= table_start) {
+               ERROR("read_filesystem_tables: uid start too large in super block\n");
+               goto corrupted;
+       }
+
+       /* There should be at least one uid */
+       if(sBlk.no_uids == 0) {
+               ERROR("read_filesystem_tables: uid count bad in super block\n");
+               goto corrupted;
+       }
+
+       if(read_ids(sBlk.no_uids, sBlk.uid_start, table_start, &uid_table) == FALSE)
+               goto corrupted;
+
+       table_start = sBlk.uid_start;
+
+       /* Read exports table */
+       if(sBlk.s.lookup_table_start != SQUASHFS_INVALID_BLK) {
+
+               /* sanity check super block contents */
+               if(sBlk.s.lookup_table_start >= table_start) {
+                       ERROR("read_filesystem_tables: lookup table start too large in super block\n");
+                       goto corrupted;
+               }
+
+               if(parse_exports_table(&table_start) == FALSE)
+                       goto corrupted;
+       }
+
+       /* Read fragment table */
+       if(sBlk.s.fragments != 0) {
+
+               /* Sanity check super block contents */
+               if(sBlk.s.fragment_table_start >= table_start) {
+                       ERROR("read_filesystem_tables: fragment table start too large in super block\n");
+                       goto corrupted;
+               }
+
+               /* The number of fragments should not exceed the number of inodes */
+               if(sBlk.s.fragments > sBlk.s.inodes) {
+                       ERROR("read_filesystem_tables: Bad fragment count in super block\n");
+                       goto corrupted;
+               }
+
+               if(read_fragment_table(&table_start) == FALSE)
+                       goto corrupted;
+       } else {
+               /*
+                * Sanity check super block contents - with 0 fragments,
+                * the fragment table should be empty
+                */
+               if(sBlk.s.fragment_table_start != table_start) {
+                       ERROR("read_filesystem_tables: fragment table start invalid in super block\n");
+                       goto corrupted;
+               }
+       }
+
+       /* Read directory table */
+
+       /* Sanity check super block contents */
+       if(sBlk.s.directory_table_start > table_start) {
+               ERROR("read_filesystem_tables: directory table start too large in super block\n");
+               goto corrupted;
+       }
+
+       directory_table = read_directory_table(sBlk.s.directory_table_start,
+                               table_start);
+       if(directory_table == NULL)
+               goto corrupted;
+
+       /* Read inode table */
+
+       /* Sanity check super block contents */
+       if(sBlk.s.inode_table_start >= sBlk.s.directory_table_start) {
+               ERROR("read_filesystem_tables: inode table start too large in super block\n");
+               goto corrupted;
+       }
+
+       inode_table = read_inode_table(sBlk.s.inode_table_start,
+                               sBlk.s.directory_table_start);
+       if(inode_table == NULL)
+               goto corrupted;
+
+       alloc_index_table(0);
+       salloc_index_table(0);
+
+       return &ops;
+
+corrupted:
+       ERROR("File system corruption detected\n");
+       alloc_index_table(0);
+       salloc_index_table(0);
+
+       return NULL;
+}
+
+
+static squashfs_operations ops = {
+       .opendir = squashfs_opendir,
+       .read_fragment = read_fragment,
+       .read_block_list = read_block_list,
+       .read_inode = read_inode
+};
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-34.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-34.c
new file mode 100644 (file)
index 0000000..0e4471e
--- /dev/null
@@ -0,0 +1,47 @@
+/*
+ * Unsquash a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * unsquash-34.c
+ *
+ * Helper functions used by unsquash-3 and unsquash-4.
+ */
+
+#include "unsquashfs.h"
+
+long long *alloc_index_table(int indexes)
+{
+       static long long *alloc_table = NULL;
+       static int alloc_size = 0;
+       int length = indexes * sizeof(long long);
+
+       if(alloc_size < length || length == 0) {
+               long long *table = realloc(alloc_table, length);
+
+               if(table == NULL && length !=0)
+                       EXIT_UNSQUASH("alloc_index_table: failed to allocate "
+                               "index table\n");
+
+               alloc_table = table;
+               alloc_size = length;
+       }
+
+       return alloc_table;
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-4.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquash-4.c
new file mode 100644 (file)
index 0000000..8475835
--- /dev/null
@@ -0,0 +1,621 @@
+/*
+ * Unsquash a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2009, 2010, 2011, 2012, 2013, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * unsquash-4.c
+ */
+
+#include "unsquashfs.h"
+#include "squashfs_swap.h"
+#include "xattr.h"
+
+static struct squashfs_fragment_entry *fragment_table;
+static unsigned int *id_table;
+static char *inode_table, *directory_table;
+static squashfs_operations ops;
+
+static void read_block_list(unsigned int *block_list, char *block_ptr, int blocks)
+{
+       TRACE("read_block_list: blocks %d\n", blocks);
+
+       memcpy(block_list, block_ptr, blocks * sizeof(unsigned int));
+       SQUASHFS_INSWAP_INTS(block_list, blocks);
+}
+
+
+static int read_fragment_table(long long *table_start)
+{
+       /*
+        * Note on overflow limits:
+        * Size of SBlk.s.fragments is 2^32 (unsigned int)
+        * Max size of bytes is 2^32*16 or 2^36
+        * Max indexes is (2^32*16)/8K or 2^23
+        * Max length is ((2^32*16)/8K)*8 or 2^26 or 64M
+        */
+       int res, i;
+       long long bytes = SQUASHFS_FRAGMENT_BYTES((long long) sBlk.s.fragments);
+       int indexes = SQUASHFS_FRAGMENT_INDEXES((long long) sBlk.s.fragments);
+       int length = SQUASHFS_FRAGMENT_INDEX_BYTES((long long) sBlk.s.fragments);
+       long long *fragment_table_index;
+
+       /*
+        * The size of the index table (length bytes) should match the
+        * table start and end points
+        */
+       if(length != (*table_start - sBlk.s.fragment_table_start)) {
+               ERROR("read_fragment_table: Bad fragment count in super block\n");
+               return FALSE;
+       }
+
+       TRACE("read_fragment_table: %d fragments, reading %d fragment indexes "
+               "from 0x%llx\n", sBlk.s.fragments, indexes,
+               sBlk.s.fragment_table_start);
+
+       fragment_table_index = alloc_index_table(indexes);
+       fragment_table = malloc(bytes);
+       if(fragment_table == NULL)
+               EXIT_UNSQUASH("read_fragment_table: failed to allocate "
+                       "fragment table\n");
+
+       res = read_fs_bytes(fd, sBlk.s.fragment_table_start, length,
+                                                       fragment_table_index);
+       if(res == FALSE) {
+               ERROR("read_fragment_table: failed to read fragment table "
+                       "index\n");
+               return FALSE;
+       }
+       SQUASHFS_INSWAP_FRAGMENT_INDEXES(fragment_table_index, indexes);
+
+       for(i = 0; i < indexes; i++) {
+               int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
+                                       bytes & (SQUASHFS_METADATA_SIZE - 1);
+               int length = read_block(fd, fragment_table_index[i], NULL,
+                       expected, ((char *) fragment_table) + (i *
+                       SQUASHFS_METADATA_SIZE));
+               TRACE("Read fragment table block %d, from 0x%llx, length %d\n",
+                       i, fragment_table_index[i], length);
+               if(length == FALSE) {
+                       ERROR("read_fragment_table: failed to read fragment "
+                               "table index\n");
+                       return FALSE;
+               }
+       }
+
+       for(i = 0; i < sBlk.s.fragments; i++) 
+               SQUASHFS_INSWAP_FRAGMENT_ENTRY(&fragment_table[i]);
+
+       *table_start = fragment_table_index[0];
+       return TRUE;
+}
+
+
+static void read_fragment(unsigned int fragment, long long *start_block, int *size)
+{
+       TRACE("read_fragment: reading fragment %d\n", fragment);
+
+       struct squashfs_fragment_entry *fragment_entry;
+
+       fragment_entry = &fragment_table[fragment];
+       *start_block = fragment_entry->start_block;
+       *size = fragment_entry->size;
+}
+
+
+static struct inode *read_inode(unsigned int start_block, unsigned int offset)
+{
+       static union squashfs_inode_header header;
+       long long start = sBlk.s.inode_table_start + start_block;
+       long long bytes = lookup_entry(inode_table_hash, start);
+       char *block_ptr = inode_table + bytes + offset;
+       static struct inode i;
+
+       TRACE("read_inode: reading inode [%d:%d]\n", start_block,  offset);
+
+       if(bytes == -1)
+               EXIT_UNSQUASH("read_inode: inode table block %lld not found\n",
+                       start);                 
+
+       SQUASHFS_SWAP_BASE_INODE_HEADER(block_ptr, &header.base);
+
+       i.uid = (uid_t) id_table[header.base.uid];
+       i.gid = (uid_t) id_table[header.base.guid];
+       i.mode = lookup_type[header.base.inode_type] | header.base.mode;
+       i.type = header.base.inode_type;
+       i.time = header.base.mtime;
+       i.inode_number = header.base.inode_number;
+
+       switch(header.base.inode_type) {
+               case SQUASHFS_DIR_TYPE: {
+                       struct squashfs_dir_inode_header *inode = &header.dir;
+
+                       SQUASHFS_SWAP_DIR_INODE_HEADER(block_ptr, inode);
+
+                       i.data = inode->file_size;
+                       i.offset = inode->offset;
+                       i.start = inode->start_block;
+                       i.xattr = SQUASHFS_INVALID_XATTR;
+                       break;
+               }
+               case SQUASHFS_LDIR_TYPE: {
+                       struct squashfs_ldir_inode_header *inode = &header.ldir;
+
+                       SQUASHFS_SWAP_LDIR_INODE_HEADER(block_ptr, inode);
+
+                       i.data = inode->file_size;
+                       i.offset = inode->offset;
+                       i.start = inode->start_block;
+                       i.xattr = inode->xattr;
+                       break;
+               }
+               case SQUASHFS_FILE_TYPE: {
+                       struct squashfs_reg_inode_header *inode = &header.reg;
+
+                       SQUASHFS_SWAP_REG_INODE_HEADER(block_ptr, inode);
+
+                       i.data = inode->file_size;
+                       i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
+                               ?  0 : inode->file_size % sBlk.s.block_size;
+                       i.fragment = inode->fragment;
+                       i.offset = inode->offset;
+                       i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
+                               (i.data + sBlk.s.block_size - 1) >>
+                               sBlk.s.block_log :
+                               i.data >> sBlk.s.block_log;
+                       i.start = inode->start_block;
+                       i.sparse = 0;
+                       i.block_ptr = block_ptr + sizeof(*inode);
+                       i.xattr = SQUASHFS_INVALID_XATTR;
+                       break;
+               }       
+               case SQUASHFS_LREG_TYPE: {
+                       struct squashfs_lreg_inode_header *inode = &header.lreg;
+
+                       SQUASHFS_SWAP_LREG_INODE_HEADER(block_ptr, inode);
+
+                       i.data = inode->file_size;
+                       i.frag_bytes = inode->fragment == SQUASHFS_INVALID_FRAG
+                               ?  0 : inode->file_size % sBlk.s.block_size;
+                       i.fragment = inode->fragment;
+                       i.offset = inode->offset;
+                       i.blocks = inode->fragment == SQUASHFS_INVALID_FRAG ?
+                               (inode->file_size + sBlk.s.block_size - 1) >>
+                               sBlk.s.block_log :
+                               inode->file_size >> sBlk.s.block_log;
+                       i.start = inode->start_block;
+                       i.sparse = inode->sparse != 0;
+                       i.block_ptr = block_ptr + sizeof(*inode);
+                       i.xattr = inode->xattr;
+                       break;
+               }       
+               case SQUASHFS_SYMLINK_TYPE:
+               case SQUASHFS_LSYMLINK_TYPE: {
+                       struct squashfs_symlink_inode_header *inode = &header.symlink;
+
+                       SQUASHFS_SWAP_SYMLINK_INODE_HEADER(block_ptr, inode);
+
+                       i.symlink = malloc(inode->symlink_size + 1);
+                       if(i.symlink == NULL)
+                               EXIT_UNSQUASH("read_inode: failed to malloc "
+                                       "symlink data\n");
+                       strncpy(i.symlink, block_ptr +
+                               sizeof(struct squashfs_symlink_inode_header),
+                               inode->symlink_size);
+                       i.symlink[inode->symlink_size] = '\0';
+                       i.data = inode->symlink_size;
+
+                       if(header.base.inode_type == SQUASHFS_LSYMLINK_TYPE)
+                               SQUASHFS_SWAP_INTS(block_ptr +
+                                       sizeof(struct squashfs_symlink_inode_header) +
+                                       inode->symlink_size, &i.xattr, 1);
+                       else
+                               i.xattr = SQUASHFS_INVALID_XATTR;
+                       break;
+               }
+               case SQUASHFS_BLKDEV_TYPE:
+               case SQUASHFS_CHRDEV_TYPE: {
+                       struct squashfs_dev_inode_header *inode = &header.dev;
+
+                       SQUASHFS_SWAP_DEV_INODE_HEADER(block_ptr, inode);
+
+                       i.data = inode->rdev;
+                       i.xattr = SQUASHFS_INVALID_XATTR;
+                       break;
+               }
+               case SQUASHFS_LBLKDEV_TYPE:
+               case SQUASHFS_LCHRDEV_TYPE: {
+                       struct squashfs_ldev_inode_header *inode = &header.ldev;
+
+                       SQUASHFS_SWAP_LDEV_INODE_HEADER(block_ptr, inode);
+
+                       i.data = inode->rdev;
+                       i.xattr = inode->xattr;
+                       break;
+               }
+               case SQUASHFS_FIFO_TYPE:
+               case SQUASHFS_SOCKET_TYPE:
+                       i.data = 0;
+                       i.xattr = SQUASHFS_INVALID_XATTR;
+                       break;
+               case SQUASHFS_LFIFO_TYPE:
+               case SQUASHFS_LSOCKET_TYPE: {
+                       struct squashfs_lipc_inode_header *inode = &header.lipc;
+
+                       SQUASHFS_SWAP_LIPC_INODE_HEADER(block_ptr, inode);
+
+                       i.data = 0;
+                       i.xattr = inode->xattr;
+                       break;
+               }
+               default:
+                       EXIT_UNSQUASH("Unknown inode type %d in read_inode!\n",
+                               header.base.inode_type);
+       }
+       return &i;
+}
+
+
+static struct dir *squashfs_opendir(unsigned int block_start, unsigned int offset,
+       struct inode **i)
+{
+       struct squashfs_dir_header dirh;
+       char buffer[sizeof(struct squashfs_dir_entry) + SQUASHFS_NAME_LEN + 1]
+               __attribute__((aligned));
+       struct squashfs_dir_entry *dire = (struct squashfs_dir_entry *) buffer;
+       long long start;
+       long long bytes;
+       int dir_count, size;
+       struct dir_ent *new_dir;
+       struct dir *dir;
+
+       TRACE("squashfs_opendir: inode start block %d, offset %d\n",
+               block_start, offset);
+
+       *i = read_inode(block_start, offset);
+
+       dir = malloc(sizeof(struct dir));
+       if(dir == NULL)
+               EXIT_UNSQUASH("squashfs_opendir: malloc failed!\n");
+
+       dir->dir_count = 0;
+       dir->cur_entry = 0;
+       dir->mode = (*i)->mode;
+       dir->uid = (*i)->uid;
+       dir->guid = (*i)->gid;
+       dir->mtime = (*i)->time;
+       dir->xattr = (*i)->xattr;
+       dir->dirs = NULL;
+
+       if ((*i)->data == 3)
+               /*
+                * if the directory is empty, skip the unnecessary
+                * lookup_entry, this fixes the corner case with
+                * completely empty filesystems where lookup_entry correctly
+                * returning -1 is incorrectly treated as an error
+                */
+               return dir;
+
+       start = sBlk.s.directory_table_start + (*i)->start;
+       bytes = lookup_entry(directory_table_hash, start);
+
+       if(bytes == -1)
+               EXIT_UNSQUASH("squashfs_opendir: directory block %lld not "
+                       "found!\n", start);
+
+       bytes += (*i)->offset;
+       size = (*i)->data + bytes - 3;
+
+       while(bytes < size) {                   
+               SQUASHFS_SWAP_DIR_HEADER(directory_table + bytes, &dirh);
+       
+               dir_count = dirh.count + 1;
+               TRACE("squashfs_opendir: Read directory header @ byte position "
+                       "%d, %d directory entries\n", bytes, dir_count);
+               bytes += sizeof(dirh);
+
+               /* dir_count should never be larger than SQUASHFS_DIR_COUNT */
+               if(dir_count > SQUASHFS_DIR_COUNT) {
+                       ERROR("File system corrupted: too many entries in directory\n");
+                       goto corrupted;
+               }
+
+               while(dir_count--) {
+                       SQUASHFS_SWAP_DIR_ENTRY(directory_table + bytes, dire);
+
+                       bytes += sizeof(*dire);
+
+                       /* size should never be SQUASHFS_NAME_LEN or larger */
+                       if(dire->size >= SQUASHFS_NAME_LEN) {
+                               ERROR("File system corrupted: filename too long\n");
+                               goto corrupted;
+                       }
+
+                       memcpy(dire->name, directory_table + bytes,
+                               dire->size + 1);
+                       dire->name[dire->size + 1] = '\0';
+                       TRACE("squashfs_opendir: directory entry %s, inode "
+                               "%d:%d, type %d\n", dire->name,
+                               dirh.start_block, dire->offset, dire->type);
+                       if((dir->dir_count % DIR_ENT_SIZE) == 0) {
+                               new_dir = realloc(dir->dirs, (dir->dir_count +
+                                       DIR_ENT_SIZE) * sizeof(struct dir_ent));
+                               if(new_dir == NULL)
+                                       EXIT_UNSQUASH("squashfs_opendir: "
+                                               "realloc failed!\n");
+                               dir->dirs = new_dir;
+                       }
+                       strcpy(dir->dirs[dir->dir_count].name, dire->name);
+                       dir->dirs[dir->dir_count].start_block =
+                               dirh.start_block;
+                       dir->dirs[dir->dir_count].offset = dire->offset;
+                       dir->dirs[dir->dir_count].type = dire->type;
+                       dir->dir_count ++;
+                       bytes += dire->size + 1;
+               }
+       }
+
+       return dir;
+
+corrupted:
+       free(dir->dirs);
+       free(dir);
+       return NULL;
+}
+
+
+static int read_id_table(long long *table_start)
+{
+       /*
+        * Note on overflow limits:
+        * Size of SBlk.s.no_ids is 2^16 (unsigned short)
+        * Max size of bytes is 2^16*4 or 256K
+        * Max indexes is (2^16*4)/8K or 32
+        * Max length is ((2^16*4)/8K)*8 or 256
+        */
+       int res, i;
+       int bytes = SQUASHFS_ID_BYTES(sBlk.s.no_ids);
+       int indexes = SQUASHFS_ID_BLOCKS(sBlk.s.no_ids);
+       int length = SQUASHFS_ID_BLOCK_BYTES(sBlk.s.no_ids);
+       long long *id_index_table;
+
+       /*
+        * The size of the index table (length bytes) should match the
+        * table start and end points
+        */
+       if(length != (*table_start - sBlk.s.id_table_start)) {
+               ERROR("read_id_table: Bad id count in super block\n");
+               return FALSE;
+       }
+
+       TRACE("read_id_table: no_ids %d\n", sBlk.s.no_ids);
+
+       id_index_table = alloc_index_table(indexes);
+       id_table = malloc(bytes);
+       if(id_table == NULL) {
+               ERROR("read_id_table: failed to allocate id table\n");
+               return FALSE;
+       }
+
+       res = read_fs_bytes(fd, sBlk.s.id_table_start, length, id_index_table);
+       if(res == FALSE) {
+               ERROR("read_id_table: failed to read id index table\n");
+               return FALSE;
+       }
+       SQUASHFS_INSWAP_ID_BLOCKS(id_index_table, indexes);
+
+       /*
+        * id_index_table[0] stores the start of the compressed id blocks.
+        * This by definition is also the end of the previous filesystem
+        * table - this may be the exports table if it is present, or the
+        * fragments table if it isn't.
+        */
+       *table_start = id_index_table[0];
+
+       for(i = 0; i < indexes; i++) {
+               int expected = (i + 1) != indexes ? SQUASHFS_METADATA_SIZE :
+                                       bytes & (SQUASHFS_METADATA_SIZE - 1);
+               res = read_block(fd, id_index_table[i], NULL, expected,
+                       ((char *) id_table) + i * SQUASHFS_METADATA_SIZE);
+               if(res == FALSE) {
+                       ERROR("read_id_table: failed to read id table block"
+                               "\n");
+                       return FALSE;
+               }
+       }
+
+       SQUASHFS_INSWAP_INTS(id_table, sBlk.s.no_ids);
+
+       return TRUE;
+}
+
+
+static int parse_exports_table(long long *table_start)
+{
+       /*
+        * Note on overflow limits:
+        * Size of SBlk.s.inodes is 2^32 (unsigned int)
+        * Max indexes is (2^32*8)/8K or 2^22
+        * Max length is ((2^32*8)/8K)*8 or 2^25
+        */
+       int res;
+       int indexes = SQUASHFS_LOOKUP_BLOCKS((long long) sBlk.s.inodes);
+       int length = SQUASHFS_LOOKUP_BLOCK_BYTES((long long) sBlk.s.inodes);
+       long long *export_index_table;
+
+       /*
+        * The size of the index table (length bytes) should match the
+        * table start and end points
+        */
+       if(length != (*table_start - sBlk.s.lookup_table_start)) {
+               ERROR("parse_exports_table: Bad inode count in super block\n");
+               return FALSE;
+       }
+
+       export_index_table = alloc_index_table(indexes);
+
+       res = read_fs_bytes(fd, sBlk.s.lookup_table_start, length,
+                                                       export_index_table);
+       if(res == FALSE) {
+               ERROR("parse_exports_table: failed to read export index table\n");
+               return FALSE;
+       }
+       SQUASHFS_INSWAP_LOOKUP_BLOCKS(export_index_table, indexes);
+
+       /*
+        * export_index_table[0] stores the start of the compressed export blocks.
+        * This by definition is also the end of the previous filesystem
+        * table - the fragment table.
+        */
+       *table_start = export_index_table[0];
+
+       return TRUE;
+}
+
+
+squashfs_operations *read_filesystem_tables_4()
+{
+       long long table_start;
+
+       /* Read xattrs */
+       if(sBlk.s.xattr_id_table_start != SQUASHFS_INVALID_BLK) {
+               /* sanity check super block contents */
+               if(sBlk.s.xattr_id_table_start >= sBlk.s.bytes_used) {
+                       ERROR("read_filesystem_tables: xattr id table start too large in super block\n");
+                       goto corrupted;
+               }
+
+               if(read_xattrs_from_disk(fd, &sBlk.s, no_xattrs, &table_start) == 0)
+                       goto corrupted;
+       } else
+               table_start = sBlk.s.bytes_used;
+
+       /* Read id lookup table */
+
+       /* Sanity check super block contents */
+       if(sBlk.s.id_table_start >= table_start) {
+               ERROR("read_filesystem_tables: id table start too large in super block\n");
+               goto corrupted;
+       }
+
+       /* there should always be at least one id */
+       if(sBlk.s.no_ids == 0) {
+               ERROR("read_filesystem_tables: Bad id count in super block\n");
+               goto corrupted;
+       }
+
+       /*
+        * the number of ids can never be more than double the number of inodes
+        * (the maximum is a unique uid and gid for each inode).
+        */
+       if(sBlk.s.no_ids > (sBlk.s.inodes * 2L)) {
+               ERROR("read_filesystem_tables: Bad id count in super block\n");
+               goto corrupted;
+       }
+
+       if(read_id_table(&table_start) == FALSE)
+               goto corrupted;
+
+       /* Read exports table */
+       if(sBlk.s.lookup_table_start != SQUASHFS_INVALID_BLK) {
+
+               /* sanity check super block contents */
+               if(sBlk.s.lookup_table_start >= table_start) {
+                       ERROR("read_filesystem_tables: lookup table start too large in super block\n");
+                       goto corrupted;
+               }
+
+               if(parse_exports_table(&table_start) == FALSE)
+                       goto corrupted;
+       }
+
+       /* Read fragment table */
+       if(sBlk.s.fragments != 0) {
+
+               /* Sanity check super block contents */
+               if(sBlk.s.fragment_table_start >= table_start) {
+                       ERROR("read_filesystem_tables: fragment table start too large in super block\n");
+                       goto corrupted;
+               }
+
+               /* The number of fragments should not exceed the number of inodes */
+               if(sBlk.s.fragments > sBlk.s.inodes) {
+                       ERROR("read_filesystem_tables: Bad fragment count in super block\n");
+                       goto corrupted;
+               }
+
+               if(read_fragment_table(&table_start) == FALSE)
+                       goto corrupted;
+       } else {
+               /*
+                * Sanity check super block contents - with 0 fragments,
+                * the fragment table should be empty
+                */
+               if(sBlk.s.fragment_table_start != table_start) {
+                       ERROR("read_filesystem_tables: fragment table start invalid in super block\n");
+                       goto corrupted;
+               }
+       }
+
+       /* Read directory table */
+
+       /* Sanity check super block contents */
+       if(sBlk.s.directory_table_start > table_start) {
+               ERROR("read_filesystem_tables: directory table start too large in super block\n");
+               goto corrupted;
+       }
+
+       directory_table = read_directory_table(sBlk.s.directory_table_start,
+                               table_start);
+       if(directory_table == NULL)
+               goto corrupted;
+
+       /* Read inode table */
+
+       /* Sanity check super block contents */
+       if(sBlk.s.inode_table_start >= sBlk.s.directory_table_start) {
+               ERROR("read_filesystem_tables: inode table start too large in super block\n");
+               goto corrupted;
+       }
+
+       inode_table = read_inode_table(sBlk.s.inode_table_start,
+                               sBlk.s.directory_table_start);
+       if(inode_table == NULL)
+               goto corrupted;
+
+       if(no_xattrs)
+               sBlk.s.xattr_id_table_start = SQUASHFS_INVALID_BLK;
+
+       alloc_index_table(0);
+
+       return &ops;
+
+corrupted:
+       ERROR("File system corruption detected\n");
+       alloc_index_table(0);
+
+       return NULL;
+}
+
+
+static squashfs_operations ops = {
+       .opendir = squashfs_opendir,
+       .read_fragment = read_fragment,
+       .read_block_list = read_block_list,
+       .read_inode = read_inode
+};
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquashfs.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquashfs.c
new file mode 100644 (file)
index 0000000..2fc549a
--- /dev/null
@@ -0,0 +1,3159 @@
+/*
+ * Unsquash a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011,
+ * 2012, 2013, 2014, 2017, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * unsquashfs.c
+ */
+
+#include "unsquashfs.h"
+#include "squashfs_swap.h"
+#include "squashfs_compat.h"
+#include "compressor.h"
+#include "xattr.h"
+#include "unsquashfs_info.h"
+#include "stdarg.h"
+#include "fnmatch_compat.h"
+
+#include <sys/sysinfo.h>
+#include <sys/sysmacros.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <limits.h>
+#include <ctype.h>
+
+
+typedef unsigned long long uint64_t;
+typedef unsigned int    uint32_t;
+
+static int verbose = 0;
+#define debug(fmt, ...) if(verbose) printf(fmt, ##__VA_ARGS__)
+
+#pragma pack(1)
+
+typedef struct fs_disk_region
+{
+    uint32_t sector;
+    uint32_t count;
+}fs_disk_region;
+
+typedef struct fs_disk_map
+{
+    char diskname[32];
+    uint64_t filesize;
+    
+    //fs_disk_region[N];
+ }fs_disk_map;
+#pragma pack()
+
+int g_fs_region_num = 0;
+fs_disk_region *g_fs_region_list = NULL;
+fs_disk_map g_fs_disk_map;
+
+struct cache *fragment_cache, *data_cache;
+struct queue *to_reader, *to_inflate, *to_writer, *from_writer;
+pthread_t *thread, *inflator_thread;
+pthread_mutex_t        fragment_mutex;
+static long long start_offset = 0;
+
+/* user options that control parallelisation */
+int processors = -1;
+
+struct super_block sBlk;
+squashfs_operations *s_ops;
+squashfs_operations *(*read_filesystem_tables)();
+struct compressor *comp;
+
+int bytes = 0, swap, file_count = 0, dir_count = 0, sym_count = 0,
+       dev_count = 0, fifo_count = 0;
+struct hash_table_entry *inode_table_hash[65536], *directory_table_hash[65536];
+int fd;
+unsigned int cached_frag = SQUASHFS_INVALID_FRAG;
+unsigned int block_size;
+unsigned int block_log;
+int lsonly = FALSE, info = FALSE, force = FALSE, short_ls = TRUE;
+int concise = FALSE, quiet = FALSE, numeric = FALSE;
+int use_regex = FALSE;
+char **created_inode;
+int root_process;
+int columns;
+int rotate = 0;
+pthread_mutex_t        screen_mutex;
+int progress = TRUE, progress_enabled = FALSE;
+unsigned int total_blocks = 0, total_files = 0, total_inodes = 0;
+unsigned int cur_blocks = 0;
+int inode_number = 1;
+int no_xattrs = XATTR_DEF;
+int user_xattrs = FALSE;
+int ignore_errors = FALSE;
+int strict_errors = FALSE;
+int use_localtime = TRUE;
+
+int lookup_type[] = {
+       0,
+       S_IFDIR,
+       S_IFREG,
+       S_IFLNK,
+       S_IFBLK,
+       S_IFCHR,
+       S_IFIFO,
+       S_IFSOCK,
+       S_IFDIR,
+       S_IFREG,
+       S_IFLNK,
+       S_IFBLK,
+       S_IFCHR,
+       S_IFIFO,
+       S_IFSOCK
+};
+
+struct test table[] = {
+       { S_IFMT, S_IFSOCK, 0, 's' },
+       { S_IFMT, S_IFLNK, 0, 'l' },
+       { S_IFMT, S_IFBLK, 0, 'b' },
+       { S_IFMT, S_IFDIR, 0, 'd' },
+       { S_IFMT, S_IFCHR, 0, 'c' },
+       { S_IFMT, S_IFIFO, 0, 'p' },
+       { S_IRUSR, S_IRUSR, 1, 'r' },
+       { S_IWUSR, S_IWUSR, 2, 'w' },
+       { S_IRGRP, S_IRGRP, 4, 'r' },
+       { S_IWGRP, S_IWGRP, 5, 'w' },
+       { S_IROTH, S_IROTH, 7, 'r' },
+       { S_IWOTH, S_IWOTH, 8, 'w' },
+       { S_IXUSR | S_ISUID, S_IXUSR | S_ISUID, 3, 's' },
+       { S_IXUSR | S_ISUID, S_ISUID, 3, 'S' },
+       { S_IXUSR | S_ISUID, S_IXUSR, 3, 'x' },
+       { S_IXGRP | S_ISGID, S_IXGRP | S_ISGID, 6, 's' },
+       { S_IXGRP | S_ISGID, S_ISGID, 6, 'S' },
+       { S_IXGRP | S_ISGID, S_IXGRP, 6, 'x' },
+       { S_IXOTH | S_ISVTX, S_IXOTH | S_ISVTX, 9, 't' },
+       { S_IXOTH | S_ISVTX, S_ISVTX, 9, 'T' },
+       { S_IXOTH | S_ISVTX, S_IXOTH, 9, 'x' },
+       { 0, 0, 0, 0}
+};
+
+void progress_bar(long long current, long long max, int columns);
+
+#define MAX_LINE 16384
+
+void prep_exit()
+{
+}
+
+
+void sigwinch_handler()
+{
+       struct winsize winsize;
+
+       if(ioctl(1, TIOCGWINSZ, &winsize) == -1) {
+               if(isatty(STDOUT_FILENO))
+                       ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 "
+                               "columns\n");
+               columns = 80;
+       } else
+               columns = winsize.ws_col;
+}
+
+
+void sigalrm_handler()
+{
+       rotate = (rotate + 1) % 4;
+}
+
+
+int add_overflow(int a, int b)
+{
+       return (INT_MAX - a) < b;
+}
+
+
+int shift_overflow(int a, int shift)
+{
+       return (INT_MAX >> shift) < a;
+}
+
+int multiply_overflow(int a, int multiplier)
+{
+       return (INT_MAX / multiplier) < a;
+}
+
+
+struct queue *queue_init(int size)
+{
+       struct queue *queue = malloc(sizeof(struct queue));
+
+       if(queue == NULL)
+               EXIT_UNSQUASH("Out of memory in queue_init\n");
+
+       if(add_overflow(size, 1) ||
+                               multiply_overflow(size + 1, sizeof(void *)))
+               EXIT_UNSQUASH("Size too large in queue_init\n");
+
+       queue->data = malloc(sizeof(void *) * (size + 1));
+       if(queue->data == NULL)
+               EXIT_UNSQUASH("Out of memory in queue_init\n");
+
+       queue->size = size + 1;
+       queue->readp = queue->writep = 0;
+       pthread_mutex_init(&queue->mutex, NULL);
+       pthread_cond_init(&queue->empty, NULL);
+       pthread_cond_init(&queue->full, NULL);
+
+       return queue;
+}
+
+
+void queue_put(struct queue *queue, void *data)
+{
+       int nextp;
+
+       pthread_mutex_lock(&queue->mutex);
+
+       while((nextp = (queue->writep + 1) % queue->size) == queue->readp)
+               pthread_cond_wait(&queue->full, &queue->mutex);
+
+       queue->data[queue->writep] = data;
+       queue->writep = nextp;
+       pthread_cond_signal(&queue->empty);
+       pthread_mutex_unlock(&queue->mutex);
+}
+
+
+void *queue_get(struct queue *queue)
+{
+       void *data;
+       pthread_mutex_lock(&queue->mutex);
+
+       while(queue->readp == queue->writep)
+               pthread_cond_wait(&queue->empty, &queue->mutex);
+
+       data = queue->data[queue->readp];
+       queue->readp = (queue->readp + 1) % queue->size;
+       pthread_cond_signal(&queue->full);
+       pthread_mutex_unlock(&queue->mutex);
+
+       return data;
+}
+
+
+void dump_queue(struct queue *queue)
+{
+       pthread_mutex_lock(&queue->mutex);
+
+       printf("Max size %d, size %d%s\n", queue->size - 1,  
+               queue->readp <= queue->writep ? queue->writep - queue->readp :
+                       queue->size - queue->readp + queue->writep,
+               queue->readp == queue->writep ? " (EMPTY)" :
+                       ((queue->writep + 1) % queue->size) == queue->readp ?
+                       " (FULL)" : "");
+
+       pthread_mutex_unlock(&queue->mutex);
+}
+
+
+/* Called with the cache mutex held */
+void insert_hash_table(struct cache *cache, struct cache_entry *entry)
+{
+       int hash = CALCULATE_HASH(entry->block);
+
+       entry->hash_next = cache->hash_table[hash];
+       cache->hash_table[hash] = entry;
+       entry->hash_prev = NULL;
+       if(entry->hash_next)
+               entry->hash_next->hash_prev = entry;
+}
+
+
+/* Called with the cache mutex held */
+void remove_hash_table(struct cache *cache, struct cache_entry *entry)
+{
+       if(entry->hash_prev)
+               entry->hash_prev->hash_next = entry->hash_next;
+       else
+               cache->hash_table[CALCULATE_HASH(entry->block)] =
+                       entry->hash_next;
+       if(entry->hash_next)
+               entry->hash_next->hash_prev = entry->hash_prev;
+
+       entry->hash_prev = entry->hash_next = NULL;
+}
+
+
+/* Called with the cache mutex held */
+void insert_free_list(struct cache *cache, struct cache_entry *entry)
+{
+       if(cache->free_list) {
+               entry->free_next = cache->free_list;
+               entry->free_prev = cache->free_list->free_prev;
+               cache->free_list->free_prev->free_next = entry;
+               cache->free_list->free_prev = entry;
+       } else {
+               cache->free_list = entry;
+               entry->free_prev = entry->free_next = entry;
+       }
+}
+
+
+/* Called with the cache mutex held */
+void remove_free_list(struct cache *cache, struct cache_entry *entry)
+{
+       if(entry->free_prev == NULL || entry->free_next == NULL)
+               /* not in free list */
+               return;
+       else if(entry->free_prev == entry && entry->free_next == entry) {
+               /* only this entry in the free list */
+               cache->free_list = NULL;
+       } else {
+               /* more than one entry in the free list */
+               entry->free_next->free_prev = entry->free_prev;
+               entry->free_prev->free_next = entry->free_next;
+               if(cache->free_list == entry)
+                       cache->free_list = entry->free_next;
+       }
+
+       entry->free_prev = entry->free_next = NULL;
+}
+
+
+struct cache *cache_init(int buffer_size, int max_buffers)
+{
+       struct cache *cache = malloc(sizeof(struct cache));
+
+       if(cache == NULL)
+               EXIT_UNSQUASH("Out of memory in cache_init\n");
+
+       cache->max_buffers = max_buffers;
+       cache->buffer_size = buffer_size;
+       cache->count = 0;
+       cache->used = 0;
+       cache->free_list = NULL;
+       memset(cache->hash_table, 0, sizeof(struct cache_entry *) * 65536);
+       cache->wait_free = FALSE;
+       cache->wait_pending = FALSE;
+       pthread_mutex_init(&cache->mutex, NULL);
+       pthread_cond_init(&cache->wait_for_free, NULL);
+       pthread_cond_init(&cache->wait_for_pending, NULL);
+
+       return cache;
+}
+
+
+struct cache_entry *cache_get(struct cache *cache, long long block, int size)
+{
+       /*
+        * Get a block out of the cache.  If the block isn't in the cache
+        * it is added and queued to the reader() and inflate() threads for
+        * reading off disk and decompression.  The cache grows until max_blocks
+        * is reached, once this occurs existing discarded blocks on the free
+        * list are reused
+        */
+       int hash = CALCULATE_HASH(block);
+       struct cache_entry *entry;
+
+       pthread_mutex_lock(&cache->mutex);
+
+       for(entry = cache->hash_table[hash]; entry; entry = entry->hash_next)
+               if(entry->block == block)
+                       break;
+
+       if(entry) {
+               /*
+                * found the block in the cache.  If the block is currently unused
+                * remove it from the free list and increment cache used count.
+                */
+               if(entry->used == 0) {
+                       cache->used ++;
+                       remove_free_list(cache, entry);
+               }
+               entry->used ++;
+               pthread_mutex_unlock(&cache->mutex);
+       } else {
+               /*
+                * not in the cache
+                *
+                * first try to allocate new block
+                */
+               if(cache->count < cache->max_buffers) {
+                       entry = malloc(sizeof(struct cache_entry));
+                       if(entry == NULL)
+                               EXIT_UNSQUASH("Out of memory in cache_get\n");
+                       entry->data = malloc(cache->buffer_size);
+                       if(entry->data == NULL)
+                               EXIT_UNSQUASH("Out of memory in cache_get\n");
+                       entry->cache = cache;
+                       entry->free_prev = entry->free_next = NULL;
+                       cache->count ++;
+               } else {
+                       /*
+                        * try to get from free list
+                        */
+                       while(cache->free_list == NULL) {
+                               cache->wait_free = TRUE;
+                               pthread_cond_wait(&cache->wait_for_free,
+                                       &cache->mutex);
+                       }
+                       entry = cache->free_list;
+                       remove_free_list(cache, entry);
+                       remove_hash_table(cache, entry);
+               }
+
+               /*
+                * Initialise block and insert into the hash table.
+                * Increment used which tracks how many buffers in the
+                * cache are actively in use (the other blocks, count - used,
+                * are in the cache and available for lookup, but can also be
+                * re-used).
+                */
+               entry->block = block;
+               entry->size = size;
+               entry->used = 1;
+               entry->error = FALSE;
+               entry->pending = TRUE;
+               insert_hash_table(cache, entry);
+               cache->used ++;
+
+               /*
+                * queue to read thread to read and ultimately (via the
+                * decompress threads) decompress the buffer
+                */
+               pthread_mutex_unlock(&cache->mutex);
+               queue_put(to_reader, entry);
+       }
+
+       return entry;
+}
+
+       
+void cache_block_ready(struct cache_entry *entry, int error)
+{
+       /*
+        * mark cache entry as being complete, reading and (if necessary)
+        * decompression has taken place, and the buffer is valid for use.
+        * If an error occurs reading or decompressing, the buffer also 
+        * becomes ready but with an error...
+        */
+       pthread_mutex_lock(&entry->cache->mutex);
+       entry->pending = FALSE;
+       entry->error = error;
+
+       /*
+        * if the wait_pending flag is set, one or more threads may be waiting
+        * on this buffer
+        */
+       if(entry->cache->wait_pending) {
+               entry->cache->wait_pending = FALSE;
+               pthread_cond_broadcast(&entry->cache->wait_for_pending);
+       }
+
+       pthread_mutex_unlock(&entry->cache->mutex);
+}
+
+
+void cache_block_wait(struct cache_entry *entry)
+{
+       /*
+        * wait for this cache entry to become ready, when reading and (if
+        * necessary) decompression has taken place
+        */
+       pthread_mutex_lock(&entry->cache->mutex);
+
+       while(entry->pending) {
+               entry->cache->wait_pending = TRUE;
+               pthread_cond_wait(&entry->cache->wait_for_pending,
+                       &entry->cache->mutex);
+       }
+
+       pthread_mutex_unlock(&entry->cache->mutex);
+}
+
+
+void cache_block_put(struct cache_entry *entry)
+{
+       /*
+        * finished with this cache entry, once the usage count reaches zero it
+        * can be reused and is put onto the free list.  As it remains
+        * accessible via the hash table it can be found getting a new lease of
+        * life before it is reused.
+        */
+       pthread_mutex_lock(&entry->cache->mutex);
+
+       entry->used --;
+       if(entry->used == 0) {
+               insert_free_list(entry->cache, entry);
+               entry->cache->used --;
+
+               /*
+                * if the wait_free flag is set, one or more threads may be
+                * waiting on this buffer
+                */
+               if(entry->cache->wait_free) {
+                       entry->cache->wait_free = FALSE;
+                       pthread_cond_broadcast(&entry->cache->wait_for_free);
+               }
+       }
+
+       pthread_mutex_unlock(&entry->cache->mutex);
+}
+
+
+void dump_cache(struct cache *cache)
+{
+       pthread_mutex_lock(&cache->mutex);
+
+       printf("Max buffers %d, Current size %d, Used %d,  %s\n",
+               cache->max_buffers, cache->count, cache->used,
+               cache->free_list ?  "Free buffers" : "No free buffers");
+
+       pthread_mutex_unlock(&cache->mutex);
+}
+
+
+char *modestr(char *str, int mode)
+{
+       int i;
+
+       strcpy(str, "----------");
+
+       for(i = 0; table[i].mask != 0; i++) {
+               if((mode & table[i].mask) == table[i].value)
+                       str[table[i].position] = table[i].mode;
+       }
+
+       return str;
+}
+
+
+#define TOTALCHARS  25
+int print_filename(char *pathname, struct inode *inode)
+{
+       char str[11], dummy[12], dummy2[12]; /* overflow safe */
+       char *userstr, *groupstr;
+       int padchars;
+       struct passwd *user;
+       struct group *group;
+       struct tm *t;
+
+       if(short_ls) {
+               printf("%s\n", pathname);
+               return 1;
+       }
+
+       user = numeric ? NULL : getpwuid(inode->uid);
+       if(user == NULL) {
+               int res = snprintf(dummy, 12, "%d", inode->uid);
+               if(res < 0)
+                       EXIT_UNSQUASH("snprintf failed in print_filename()\n");
+               else if(res >= 12)
+                       /* unsigned int shouldn't ever need more than 11 bytes
+                        * (including terminating '\0') to print in base 10 */
+                       userstr = "*";
+               else
+                       userstr = dummy;
+       } else
+               userstr = user->pw_name;
+                
+       group = numeric ? NULL : getgrgid(inode->gid);
+       if(group == NULL) {
+               int res = snprintf(dummy2, 12, "%d", inode->gid);
+               if(res < 0)
+                       EXIT_UNSQUASH("snprintf failed in print_filename()\n");
+               else if(res >= 12)
+                       /* unsigned int shouldn't ever need more than 11 bytes
+                        * (including terminating '\0') to print in base 10 */
+                       groupstr = "*";
+               else
+                       groupstr = dummy2;
+       } else
+               groupstr = group->gr_name;
+
+       printf("%s %s/%s ", modestr(str, inode->mode), userstr, groupstr);
+
+       switch(inode->mode & S_IFMT) {
+               case S_IFREG:
+               case S_IFDIR:
+               case S_IFSOCK:
+               case S_IFIFO:
+               case S_IFLNK:
+                       padchars = TOTALCHARS - strlen(userstr) -
+                               strlen(groupstr);
+
+                       printf("%*lld ", padchars > 0 ? padchars : 0,
+                               inode->data);
+                       break;
+               case S_IFCHR:
+               case S_IFBLK:
+                       padchars = TOTALCHARS - strlen(userstr) -
+                               strlen(groupstr) - 7; 
+
+                       printf("%*s%3d,%3d ", padchars > 0 ? padchars : 0, " ",
+                               (int) inode->data >> 8, (int) inode->data &
+                               0xff);
+                       break;
+       }
+
+       t = use_localtime ? localtime(&inode->time) : gmtime(&inode->time);
+
+       printf("%d-%02d-%02d %02d:%02d %s", t->tm_year + 1900, t->tm_mon + 1,
+               t->tm_mday, t->tm_hour, t->tm_min, pathname);
+       if((inode->mode & S_IFMT) == S_IFLNK)
+               printf(" -> %s", inode->symlink);
+       printf("\n");
+               
+       return 1;
+}
+       
+
+void add_entry(struct hash_table_entry *hash_table[], long long start,
+       long long bytes)
+{
+       int hash = CALCULATE_HASH(start);
+       struct hash_table_entry *hash_table_entry;
+
+       hash_table_entry = malloc(sizeof(struct hash_table_entry));
+       if(hash_table_entry == NULL)
+               EXIT_UNSQUASH("Out of memory in add_entry\n");
+
+       hash_table_entry->start = start;
+       hash_table_entry->bytes = bytes;
+       hash_table_entry->next = hash_table[hash];
+       hash_table[hash] = hash_table_entry;
+}
+
+
+long long lookup_entry(struct hash_table_entry *hash_table[], long long start)
+{
+       int hash = CALCULATE_HASH(start);
+       struct hash_table_entry *hash_table_entry;
+
+       for(hash_table_entry = hash_table[hash]; hash_table_entry;
+                               hash_table_entry = hash_table_entry->next)
+
+               if(hash_table_entry->start == start)
+                       return hash_table_entry->bytes;
+
+       return -1;
+}
+
+int read_fs_sectors(int fd, uint32_t sector, uint32_t count, char *buf)
+{
+    int i;
+    uint32_t total = 0;
+    uint32_t left = 0;
+    uint32_t offset = 0;
+    uint32_t readcnt = 0;
+    fs_disk_region *region;
+
+    for (i = 0; i < g_fs_region_num && count > 0; i++)
+    {
+        region = g_fs_region_list + i;
+
+        if (sector >= total && sector < total + region->count)
+        {
+            offset = sector - total;
+            left = region->count - offset;
+            readcnt = (count <= left) ? count : left;
+            
+            lseek(fd, (uint64_t)(offset + region->sector) * 512ULL, SEEK_SET);
+            read(fd, buf, (uint64_t)readcnt * 512ULL);
+
+            buf += (uint64_t)readcnt * 512ULL;
+            count -= readcnt;
+        }
+        else
+        {
+            total += region->count;
+        }
+    }
+
+    return 0;
+}
+
+#if 1
+int read_fs_bytes(int fd, long long byte, int bytes, void *buff)
+{
+    uint32_t mod = 0;
+    uint32_t align = 0;
+    uint32_t sector = 0;
+    uint32_t number = 0;
+    uint32_t leftsize = 0;
+       uint64_t offset = byte;
+    char *buf = (char *)buff;
+    char secbuf[512];
+
+    if (offset >= g_fs_disk_map.filesize || offset + bytes > g_fs_disk_map.filesize)
+    {
+        return FALSE;
+    }
+
+    leftsize = bytes;
+    sector = offset / 512;
+
+    mod = offset % 512;
+    if (mod > 0)
+    {
+        align = 512 - mod;
+        read_fs_sectors(fd, sector, 1, secbuf);
+
+        if (leftsize > align)
+        {
+            memcpy(buf, secbuf + mod, align);
+            buf += align;
+            offset += align;
+            sector++;
+            leftsize -= align;
+        }
+        else
+        {
+            memcpy(buf, secbuf + mod, leftsize);
+            return TRUE;
+        }
+    }
+
+    number = leftsize / 512;
+    read_fs_sectors(fd, sector, number, buf);
+    buf += number * 512;
+
+    mod = leftsize % 512;
+    if (mod > 0)
+    {
+        read_fs_sectors(fd, sector + number, 1, secbuf);
+        memcpy(buf, secbuf, mod);
+    }
+
+       return TRUE;
+}
+
+#else
+
+int read_fs_bytes(int fd, long long byte, int bytes, void *buff)
+{
+       off_t off = byte;
+       int res, count;
+
+       TRACE("read_bytes: reading from position 0x%llx, bytes %d\n", byte,
+               bytes);
+
+       if(lseek(fd, start_offset + off, SEEK_SET) == -1) {
+               ERROR("Lseek failed because %s\n", strerror(errno));
+               return FALSE;
+       }
+
+       for(count = 0; count < bytes; count += res) {
+               res = read(fd, buff + count, bytes - count);
+               if(res < 1) {
+                       if(res == 0) {
+                               ERROR("Read on filesystem failed because "
+                                       "EOF\n");
+                               return FALSE;
+                       } else if(errno != EINTR) {
+                               ERROR("Read on filesystem failed because %s\n",
+                                               strerror(errno));
+                               return FALSE;
+                       } else
+                               res = 0;
+               }
+       }
+
+       return TRUE;
+}
+#endif
+
+int read_block(int fd, long long start, long long *next, int expected,
+                                                               void *block)
+{
+       unsigned short c_byte;
+       int offset = 2, res, compressed;
+       int outlen = expected ? expected : SQUASHFS_METADATA_SIZE;
+       static char *buffer = NULL;
+
+       if(outlen > SQUASHFS_METADATA_SIZE)
+               return FALSE;
+
+       if(swap) {
+               if(read_fs_bytes(fd, start, 2, &c_byte) == FALSE)
+                       goto failed;
+               c_byte = (c_byte >> 8) | ((c_byte & 0xff) << 8);
+       } else 
+               if(read_fs_bytes(fd, start, 2, &c_byte) == FALSE)
+                       goto failed;
+
+       TRACE("read_block: block @0x%llx, %d %s bytes\n", start,
+               SQUASHFS_COMPRESSED_SIZE(c_byte), SQUASHFS_COMPRESSED(c_byte) ?
+               "compressed" : "uncompressed");
+
+       if(SQUASHFS_CHECK_DATA(sBlk.s.flags))
+               offset = 3;
+
+       compressed = SQUASHFS_COMPRESSED(c_byte);
+       c_byte = SQUASHFS_COMPRESSED_SIZE(c_byte);
+
+       /*
+        * The block size should not be larger than
+        * the uncompressed size (or max uncompressed size if
+        * expected is 0)
+        */
+       if(c_byte > outlen)
+               return FALSE;
+
+       if(compressed) {
+               int error;
+
+               if(buffer == NULL) {
+                       buffer = malloc(SQUASHFS_METADATA_SIZE);
+
+                       if(buffer == NULL)
+                               EXIT_UNSQUASH("read_block: Failed to allocate buffer\n");
+               }
+
+               res = read_fs_bytes(fd, start + offset, c_byte, buffer);
+               if(res == FALSE)
+                       goto failed;
+
+               res = compressor_uncompress(comp, block, buffer, c_byte,
+                       outlen, &error);
+
+               if(res == -1) {
+                       ERROR("%s uncompress failed with error code %d\n",
+                               comp->name, error);
+                       goto failed;
+               }
+       } else {
+               res = read_fs_bytes(fd, start + offset, c_byte, block);
+               if(res == FALSE)
+                       goto failed;
+               res = c_byte;
+       }
+
+       if(next)
+               *next = start + offset + c_byte;
+
+       /*
+        * if expected, then check the (uncompressed) return data
+        * is of the expected size
+        */
+       if(expected && expected != res)
+               return FALSE;
+       else
+               return res;
+
+failed:
+       ERROR("read_block: failed to read block @0x%llx\n", start);
+       return FALSE;
+}
+
+
+void *read_inode_table(long long start, long long end)
+{
+       int res;
+       long long size = 0;
+       long long bytes = 0;
+       void *inode_table = NULL;
+
+       TRACE("read_inode_table: start %lld, end %lld\n", start, end);
+
+       while(start < end) {
+               if(size - bytes < SQUASHFS_METADATA_SIZE) {
+                       inode_table = realloc(inode_table, size +=
+                               SQUASHFS_METADATA_SIZE);
+                       if(inode_table == NULL) {
+                               ERROR("Out of memory in read_inode_table");
+                               goto failed;
+                       }
+               }
+
+               add_entry(inode_table_hash, start, bytes);
+
+               res = read_block(fd, start, &start, 0, inode_table + bytes);
+               if(res == 0) {
+                       ERROR("read_inode_table: failed to read block\n");
+                       goto failed;
+               }
+               bytes += res;
+
+               /*
+                * If this is not the last metadata block in the inode table
+                * then it should be SQUASHFS_METADATA_SIZE in size.
+                * Note, we can't use expected in read_block() above for this
+                * because we don't know if this is the last block until
+                * after reading.
+                */
+               if(start != end && res != SQUASHFS_METADATA_SIZE) {
+                       ERROR("read_inode_table: metadata block should be %d "
+                               "bytes in length, it is %d bytes\n",
+                               SQUASHFS_METADATA_SIZE, res);
+                       
+                       goto failed;
+               }
+       }
+
+       return inode_table;
+
+failed:
+       free(inode_table);
+       return NULL;
+}
+
+
+int set_attributes(char *pathname, int mode, uid_t uid, gid_t guid, time_t time,
+       unsigned int xattr, unsigned int set_mode)
+{
+       struct utimbuf times = { time, time };
+
+       if(utime(pathname, &times) == -1) {
+               EXIT_UNSQUASH_STRICT("set_attributes: failed to set time on %s, because %s\n",
+                       pathname, strerror(errno));
+               return FALSE;
+       }
+
+       if(root_process) {
+               if(chown(pathname, uid, guid) == -1) {
+                       EXIT_UNSQUASH_STRICT("set_attributes: failed to change uid and gids "
+                               "on %s, because %s\n", pathname,
+                               strerror(errno));
+                       return FALSE;
+               }
+       } else
+               mode &= ~06000;
+
+       if((set_mode || (mode & 07000)) && chmod(pathname, (mode_t) mode) == -1) {
+               /*
+                * Some filesystems require root privileges to use the sticky
+                * bit. If we're not root and chmod() failed with EPERM when the
+                * sticky bit was included in the mode, try again without the
+                * sticky bit. Otherwise, fail with an error message.
+                */
+               if (root_process || errno != EPERM || !(mode & 01000) ||
+                               chmod(pathname, (mode_t) (mode & ~01000)) == -1) {
+                       EXIT_UNSQUASH_STRICT("set_attributes: failed to change mode %s, because %s\n",
+                               pathname, strerror(errno));
+                       return FALSE;
+               }
+       }
+
+       return write_xattr(pathname, xattr);
+}
+
+
+int write_bytes(int fd, char *buff, int bytes)
+{
+       int res, count;
+
+       for(count = 0; count < bytes; count += res) {
+               res = write(fd, buff + count, bytes - count);
+               if(res == -1) {
+                       if(errno != EINTR) {
+                               ERROR("Write on output file failed because "
+                                       "%s\n", strerror(errno));
+                               return -1;
+                       }
+                       res = 0;
+               }
+       }
+
+       return 0;
+}
+
+
+int lseek_broken = FALSE;
+char *zero_data = NULL;
+
+int write_block(int file_fd, char *buffer, int size, long long hole, int sparse)
+{
+       off_t off = hole;
+
+       if(hole) {
+               if(sparse && lseek_broken == FALSE) {
+                        int error = lseek(file_fd, off, SEEK_CUR);
+                        if(error == -1)
+                               /* failed to seek beyond end of file */
+                               lseek_broken = TRUE;
+               }
+
+               if((sparse == FALSE || lseek_broken) && zero_data == NULL) {
+                       if((zero_data = malloc(block_size)) == NULL)
+                               EXIT_UNSQUASH("write_block: failed to alloc "
+                                       "zero data block\n");
+                       memset(zero_data, 0, block_size);
+               }
+
+               if(sparse == FALSE || lseek_broken) {
+                       int blocks = (hole + block_size -1) / block_size;
+                       int avail_bytes, i;
+                       for(i = 0; i < blocks; i++, hole -= avail_bytes) {
+                               avail_bytes = hole > block_size ? block_size :
+                                       hole;
+                               if(write_bytes(file_fd, zero_data, avail_bytes)
+                                               == -1)
+                                       goto failure;
+                       }
+               }
+       }
+
+       if(write_bytes(file_fd, buffer, size) == -1)
+               goto failure;
+
+       return TRUE;
+
+failure:
+       return FALSE;
+}
+
+
+pthread_mutex_t open_mutex = PTHREAD_MUTEX_INITIALIZER;
+pthread_cond_t open_empty = PTHREAD_COND_INITIALIZER;
+int open_unlimited, open_count;
+#define OPEN_FILE_MARGIN 10
+
+
+void open_init(int count)
+{
+       open_count = count;
+       open_unlimited = count == -1;
+}
+
+
+int open_wait(char *pathname, int flags, mode_t mode)
+{
+       if (!open_unlimited) {
+               pthread_mutex_lock(&open_mutex);
+               while (open_count == 0)
+                       pthread_cond_wait(&open_empty, &open_mutex);
+               open_count --;
+               pthread_mutex_unlock(&open_mutex);
+       }
+
+       return open(pathname, flags, mode);
+}
+
+
+void close_wake(int fd)
+{
+       close(fd);
+
+       if (!open_unlimited) {
+               pthread_mutex_lock(&open_mutex);
+               open_count ++;
+               pthread_cond_signal(&open_empty);
+               pthread_mutex_unlock(&open_mutex);
+       }
+}
+
+
+void queue_file(char *pathname, int file_fd, struct inode *inode)
+{
+       struct squashfs_file *file = malloc(sizeof(struct squashfs_file));
+       if(file == NULL)
+               EXIT_UNSQUASH("queue_file: unable to malloc file\n");
+
+       file->fd = file_fd;
+       file->file_size = inode->data;
+       file->mode = inode->mode;
+       file->gid = inode->gid;
+       file->uid = inode->uid;
+       file->time = inode->time;
+       file->pathname = strdup(pathname);
+       file->blocks = inode->blocks + (inode->frag_bytes > 0);
+       file->sparse = inode->sparse;
+       file->xattr = inode->xattr;
+       queue_put(to_writer, file);
+}
+
+
+void queue_dir(char *pathname, struct dir *dir)
+{
+       struct squashfs_file *file = malloc(sizeof(struct squashfs_file));
+       if(file == NULL)
+               EXIT_UNSQUASH("queue_dir: unable to malloc file\n");
+
+       file->fd = -1;
+       file->mode = dir->mode;
+       file->gid = dir->guid;
+       file->uid = dir->uid;
+       file->time = dir->mtime;
+       file->pathname = strdup(pathname);
+       file->xattr = dir->xattr;
+       queue_put(to_writer, file);
+}
+
+
+int write_file(struct inode *inode, char *pathname)
+{
+       unsigned int file_fd, i;
+       unsigned int *block_list;
+       int file_end = inode->data / block_size;
+       long long start = inode->start;
+
+       TRACE("write_file: regular file, blocks %d\n", inode->blocks);
+
+       file_fd = open_wait(pathname, O_CREAT | O_WRONLY |
+               (force ? O_TRUNC : 0), (mode_t) inode->mode & 0777);
+       if(file_fd == -1) {
+               EXIT_UNSQUASH_IGNORE("write_file: failed to create file %s, because %s\n",
+                       pathname, strerror(errno));
+               return FALSE;
+       }
+
+       block_list = malloc(inode->blocks * sizeof(unsigned int));
+       if(block_list == NULL)
+               EXIT_UNSQUASH("write_file: unable to malloc block list\n");
+
+       s_ops->read_block_list(block_list, inode->block_ptr, inode->blocks);
+
+       /*
+        * the writer thread is queued a squashfs_file structure describing the
+        * file.  If the file has one or more blocks or a fragment they are
+        * queued separately (references to blocks in the cache).
+        */
+       queue_file(pathname, file_fd, inode);
+
+       for(i = 0; i < inode->blocks; i++) {
+               int c_byte = SQUASHFS_COMPRESSED_SIZE_BLOCK(block_list[i]);
+               struct file_entry *block = malloc(sizeof(struct file_entry));
+
+               if(block == NULL)
+                       EXIT_UNSQUASH("write_file: unable to malloc file\n");
+               block->offset = 0;
+               block->size = i == file_end ? inode->data & (block_size - 1) :
+                       block_size;
+               if(block_list[i] == 0) /* sparse block */
+                       block->buffer = NULL;
+               else {
+                       block->buffer = cache_get(data_cache, start,
+                               block_list[i]);
+                       start += c_byte;
+               }
+               queue_put(to_writer, block);
+       }
+
+       if(inode->frag_bytes) {
+               int size;
+               long long start;
+               struct file_entry *block = malloc(sizeof(struct file_entry));
+
+               if(block == NULL)
+                       EXIT_UNSQUASH("write_file: unable to malloc file\n");
+               s_ops->read_fragment(inode->fragment, &start, &size);
+               block->buffer = cache_get(fragment_cache, start, size);
+               block->offset = inode->offset;
+               block->size = inode->frag_bytes;
+               queue_put(to_writer, block);
+       }
+
+       free(block_list);
+       return TRUE;
+}
+
+
+int create_inode(char *pathname, struct inode *i)
+{
+       int res;
+       int failed = FALSE;
+
+       TRACE("create_inode: pathname %s\n", pathname);
+
+       if(created_inode[i->inode_number - 1]) {
+               TRACE("create_inode: hard link\n");
+               if(force)
+                       unlink(pathname);
+
+               if(link(created_inode[i->inode_number - 1], pathname) == -1) {
+                       EXIT_UNSQUASH_IGNORE("create_inode: failed to create hardlink, "
+                               "because %s\n", strerror(errno));
+                       return FALSE;
+               }
+
+               return TRUE;
+       }
+
+       switch(i->type) {
+               case SQUASHFS_FILE_TYPE:
+               case SQUASHFS_LREG_TYPE:
+                       TRACE("create_inode: regular file, file_size %lld, "
+                               "blocks %d\n", i->data, i->blocks);
+
+                       res = write_file(i, pathname);
+                       if(res == FALSE)
+                               goto failed;
+
+                       file_count ++;
+                       break;
+               case SQUASHFS_SYMLINK_TYPE:
+               case SQUASHFS_LSYMLINK_TYPE: {
+                       struct timespec times[2] = {
+                               { i->time, 0 },
+                               { i->time, 0 }
+                       };
+
+                       TRACE("create_inode: symlink, symlink_size %lld\n",
+                               i->data);
+
+                       if(force)
+                               unlink(pathname);
+
+                       res = symlink(i->symlink, pathname);
+                       if(res == -1) {
+                               EXIT_UNSQUASH_STRICT("create_inode: failed to create symlink "
+                                       "%s, because %s\n", pathname,
+                                       strerror(errno));
+                               goto failed;
+                       }
+
+                       res = utimensat(AT_FDCWD, pathname, times,
+                                       AT_SYMLINK_NOFOLLOW);
+                       if(res == -1) {
+                               EXIT_UNSQUASH_STRICT("create_inode: failed to set time on "
+                                       "%s, because %s\n", pathname,
+                                       strerror(errno));
+                       }
+
+                       res = write_xattr(pathname, i->xattr);
+                       if(res == FALSE)
+                               failed = TRUE;
+       
+                       if(root_process) {
+                               res = lchown(pathname, i->uid, i->gid);
+                               if(res == -1) {
+                                       EXIT_UNSQUASH_STRICT("create_inode: failed to change "
+                                               "uid and gids on %s, because "
+                                               "%s\n", pathname,
+                                               strerror(errno));
+                                       failed = TRUE;
+                               }
+                       }
+
+                       if(failed)
+                               goto failed;
+
+                       sym_count ++;
+                       break;
+               }
+               case SQUASHFS_BLKDEV_TYPE:
+               case SQUASHFS_CHRDEV_TYPE:
+               case SQUASHFS_LBLKDEV_TYPE:
+               case SQUASHFS_LCHRDEV_TYPE: {
+                       int chrdev = 0;
+                       if ( i->type == SQUASHFS_CHRDEV_TYPE ||
+                                       i->type == SQUASHFS_LCHRDEV_TYPE)
+                               chrdev = 1;
+
+                       TRACE("create_inode: dev, rdev 0x%llx\n", i->data);
+
+                       if(root_process) {
+                               if(force)
+                                       unlink(pathname);
+
+                               res = mknod(pathname, chrdev ? S_IFCHR : S_IFBLK,
+                                               makedev((i->data >> 8) & 0xff,
+                                               i->data & 0xff));
+                               if(res == -1) {
+                                       EXIT_UNSQUASH_STRICT("create_inode: failed to create "
+                                               "%s device %s, because %s\n",
+                                               chrdev ? "character" : "block",
+                                               pathname, strerror(errno));
+                                       goto failed;
+                               }
+                               res = set_attributes(pathname, i->mode, i->uid,
+                                       i->gid, i->time, i->xattr, TRUE);
+                               if(res == FALSE)
+                                       goto failed;
+
+                               dev_count ++;
+                       } else {
+                               EXIT_UNSQUASH_STRICT("create_inode: could not create %s "
+                                       "device %s, because you're not "
+                                       "superuser!\n", chrdev ? "character" :
+                                       "block", pathname);
+                               goto failed;
+                       }
+                       break;
+               }
+               case SQUASHFS_FIFO_TYPE:
+               case SQUASHFS_LFIFO_TYPE:
+                       TRACE("create_inode: fifo\n");
+
+                       if(force)
+                               unlink(pathname);
+
+                       res = mknod(pathname, S_IFIFO, 0);
+                       if(res == -1) {
+                               ERROR("create_inode: failed to create fifo %s, "
+                                       "because %s\n", pathname,
+                                       strerror(errno));
+                               goto failed;
+                       }
+                       res = set_attributes(pathname, i->mode, i->uid, i->gid,
+                               i->time, i->xattr, TRUE);
+                       if(res == FALSE)
+                               goto failed;
+
+                       fifo_count ++;
+                       break;
+               case SQUASHFS_SOCKET_TYPE:
+               case SQUASHFS_LSOCKET_TYPE:
+                       TRACE("create_inode: socket\n");
+                       ERROR("create_inode: socket %s ignored\n", pathname);
+                       break;
+               default:
+                       EXIT_UNSQUASH_STRICT("Unknown inode type %d in create_inode_table!\n",
+                               i->type);
+                       return FALSE;
+       }
+
+       created_inode[i->inode_number - 1] = strdup(pathname);
+
+       return TRUE;
+
+failed:
+       /*
+        * Mark the file as created (even though it may not have been), so
+        * any future hard links to it fail with a file not found, which
+        * is correct as the file *is* missing.
+        *
+        * If we don't mark it here as created, then any future hard links will try
+        * to create the file as a separate unlinked file.
+        * If we've had some transitory errors, this may produce files
+        * in various states, which should be hard-linked, but are not.
+        */
+       created_inode[i->inode_number - 1] = strdup(pathname);
+
+       return FALSE;
+}
+
+
+void *read_directory_table(long long start, long long end)
+{
+       int res;
+       long long bytes = 0;
+       long long size = 0;
+       void *directory_table = malloc(1);
+
+       TRACE("read_directory_table: start %lld, end %lld\n", start, end);
+
+       while(start < end) {
+               if(size - bytes < SQUASHFS_METADATA_SIZE) {
+                       directory_table = realloc(directory_table, size +=
+                               SQUASHFS_METADATA_SIZE);
+                       if(directory_table == NULL) {
+                               ERROR("Out of memory in "
+                                               "read_directory_table\n");
+                               goto failed;
+                       }
+               }
+
+               add_entry(directory_table_hash, start, bytes);
+
+               res = read_block(fd, start, &start, 0, directory_table + bytes);
+               if(res == 0) {
+                       ERROR("read_directory_table: failed to read block\n");
+                       goto failed;
+               }
+
+               bytes += res;
+
+               /*
+                * If this is not the last metadata block in the directory table
+                * then it should be SQUASHFS_METADATA_SIZE in size.
+                * Note, we can't use expected in read_block() above for this
+                * because we don't know if this is the last block until
+                * after reading.
+                */
+               if(start != end && res != SQUASHFS_METADATA_SIZE) {
+                       ERROR("read_directory_table: metadata block "
+                               "should be %d bytes in length, it is %d "
+                               "bytes\n", SQUASHFS_METADATA_SIZE, res);
+                       goto failed;
+               }
+       }
+
+       return directory_table;
+
+failed:
+       free(directory_table);
+       return NULL;
+}
+
+
+int squashfs_readdir(struct dir *dir, char **name, unsigned int *start_block,
+unsigned int *offset, unsigned int *type)
+{
+       if(dir->cur_entry == dir->dir_count)
+               return FALSE;
+
+       *name = dir->dirs[dir->cur_entry].name;
+       *start_block = dir->dirs[dir->cur_entry].start_block;
+       *offset = dir->dirs[dir->cur_entry].offset;
+       *type = dir->dirs[dir->cur_entry].type;
+       dir->cur_entry ++;
+
+       return TRUE;
+}
+
+
+void squashfs_closedir(struct dir *dir)
+{
+       free(dir->dirs);
+       free(dir);
+}
+
+
+char *get_component(char *target, char **targname)
+{
+       char *start;
+
+       while(*target == '/')
+               target ++;
+
+       start = target;
+       while(*target != '/' && *target != '\0')
+               target ++;
+
+       *targname = strndup(start, target - start);
+
+       while(*target == '/')
+               target ++;
+
+       return target;
+}
+
+
+void free_path(struct pathname *paths)
+{
+       int i;
+
+       for(i = 0; i < paths->names; i++) {
+               if(paths->name[i].paths)
+                       free_path(paths->name[i].paths);
+               free(paths->name[i].name);
+               if(paths->name[i].preg) {
+                       regfree(paths->name[i].preg);
+                       free(paths->name[i].preg);
+               }
+       }
+
+       free(paths);
+}
+
+
+struct pathname *add_path(struct pathname *paths, char *target, char *alltarget)
+{
+       char *targname;
+       int i, error;
+
+       TRACE("add_path: adding \"%s\" extract file\n", target);
+
+       target = get_component(target, &targname);
+
+       if(paths == NULL) {
+               paths = malloc(sizeof(struct pathname));
+               if(paths == NULL)
+                       EXIT_UNSQUASH("failed to allocate paths\n");
+
+               paths->names = 0;
+               paths->name = NULL;
+       }
+
+       for(i = 0; i < paths->names; i++)
+               if(strcmp(paths->name[i].name, targname) == 0)
+                       break;
+
+       if(i == paths->names) {
+               /*
+                * allocate new name entry
+                */
+               paths->names ++;
+               paths->name = realloc(paths->name, (i + 1) *
+                       sizeof(struct path_entry));
+               if(paths->name == NULL)
+                       EXIT_UNSQUASH("Out of memory in add_path\n");   
+               paths->name[i].name = targname;
+               paths->name[i].paths = NULL;
+               if(use_regex) {
+                       paths->name[i].preg = malloc(sizeof(regex_t));
+                       if(paths->name[i].preg == NULL)
+                               EXIT_UNSQUASH("Out of memory in add_path\n");
+                       error = regcomp(paths->name[i].preg, targname,
+                               REG_EXTENDED|REG_NOSUB);
+                       if(error) {
+                               char str[1024]; /* overflow safe */
+
+                               regerror(error, paths->name[i].preg, str, 1024);
+                               EXIT_UNSQUASH("invalid regex %s in export %s, "
+                                       "because %s\n", targname, alltarget,
+                                       str);
+                       }
+               } else
+                       paths->name[i].preg = NULL;
+
+               if(target[0] == '\0')
+                       /*
+                        * at leaf pathname component
+                       */
+                       paths->name[i].paths = NULL;
+               else
+                       /*
+                        * recurse adding child components
+                        */
+                       paths->name[i].paths = add_path(NULL, target, alltarget);
+       } else {
+               /*
+                * existing matching entry
+                */
+               free(targname);
+
+               if(paths->name[i].paths == NULL) {
+                       /*
+                        * No sub-directory which means this is the leaf
+                        * component of a pre-existing extract which subsumes
+                        * the extract currently being added, in which case stop
+                        * adding components
+                        */
+               } else if(target[0] == '\0') {
+                       /*
+                        * at leaf pathname component and child components exist
+                        * from more specific extracts, delete as they're
+                        * subsumed by this extract
+                        */
+                       free_path(paths->name[i].paths);
+                       paths->name[i].paths = NULL;
+               } else
+                       /*
+                        * recurse adding child components
+                        */
+                       add_path(paths->name[i].paths, target, alltarget);
+       }
+
+       return paths;
+}
+
+
+struct pathnames *init_subdir()
+{
+       struct pathnames *new = malloc(sizeof(struct pathnames));
+       if(new == NULL)
+               EXIT_UNSQUASH("Out of memory in init_subdir\n");
+       new->count = 0;
+       return new;
+}
+
+
+struct pathnames *add_subdir(struct pathnames *paths, struct pathname *path)
+{
+       if(paths->count % PATHS_ALLOC_SIZE == 0) {
+               paths = realloc(paths, sizeof(struct pathnames *) +
+                       (paths->count + PATHS_ALLOC_SIZE) *
+                       sizeof(struct pathname *));
+               if(paths == NULL)
+                       EXIT_UNSQUASH("Out of memory in add_subdir\n");
+       }
+
+       paths->path[paths->count++] = path;
+       return paths;
+}
+
+
+void free_subdir(struct pathnames *paths)
+{
+       free(paths);
+}
+
+
+int matches(struct pathnames *paths, char *name, struct pathnames **new)
+{
+       int i, n;
+
+       if(paths == NULL) {
+               *new = NULL;
+               return TRUE;
+       }
+
+       *new = init_subdir();
+
+       for(n = 0; n < paths->count; n++) {
+               struct pathname *path = paths->path[n];
+               for(i = 0; i < path->names; i++) {
+                       int match = use_regex ?
+                               regexec(path->name[i].preg, name, (size_t) 0,
+                               NULL, 0) == 0 : fnmatch(path->name[i].name,
+                               name, FNM_PATHNAME|FNM_PERIOD|FNM_EXTMATCH) ==
+                               0;
+                       if(match && path->name[i].paths == NULL)
+                               /*
+                                * match on a leaf component, any subdirectories
+                                * will implicitly match, therefore return an
+                                * empty new search set
+                                */
+                               goto empty_set;
+
+                       if(match)
+                               /*
+                                * match on a non-leaf component, add any
+                                * subdirectories to the new set of
+                                * subdirectories to scan for this name
+                                */
+                               *new = add_subdir(*new, path->name[i].paths);
+               }
+       }
+
+       if((*new)->count == 0) {
+               /*
+                * no matching names found, delete empty search set, and return
+                * FALSE
+                */
+               free_subdir(*new);
+               *new = NULL;
+               return FALSE;
+       }
+
+       /*
+        * one or more matches with sub-directories found (no leaf matches),
+        * return new search set and return TRUE
+        */
+       return TRUE;
+
+empty_set:
+       /*
+        * found matching leaf exclude, return empty search set and return TRUE
+        */
+       free_subdir(*new);
+       *new = NULL;
+       return TRUE;
+}
+
+
+int pre_scan(char *parent_name, unsigned int start_block, unsigned int offset,
+       struct pathnames *paths)
+{
+       unsigned int type;
+       int scan_res = TRUE;
+       char *name;
+       struct pathnames *new;
+       struct inode *i;
+       struct dir *dir = s_ops->opendir(start_block, offset, &i);
+
+       if(dir == NULL)
+               return FALSE;
+
+       while(squashfs_readdir(dir, &name, &start_block, &offset, &type)) {
+               struct inode *i;
+               char *pathname;
+               int res;
+
+               TRACE("pre_scan: name %s, start_block %d, offset %d, type %d\n",
+                       name, start_block, offset, type);
+
+               if(!matches(paths, name, &new))
+                       continue;
+
+               res = asprintf(&pathname, "%s/%s", parent_name, name);
+               if(res == -1)
+                       EXIT_UNSQUASH("asprintf failed in dir_scan\n");
+
+               if(type == SQUASHFS_DIR_TYPE) {
+                       res = pre_scan(parent_name, start_block, offset, new);
+                       if(res == FALSE)
+                               scan_res = FALSE;
+               } else if(new == NULL) {
+                       if(type == SQUASHFS_FILE_TYPE ||
+                                       type == SQUASHFS_LREG_TYPE) {
+                               i = s_ops->read_inode(start_block, offset);
+                               if(created_inode[i->inode_number - 1] == NULL) {
+                                       created_inode[i->inode_number - 1] =
+                                               (char *) i;
+                                       total_blocks += (i->data +
+                                               (block_size - 1)) >> block_log;
+                               }
+                               total_files ++;
+                       }
+                       total_inodes ++;
+               }
+
+               free_subdir(new);
+               free(pathname);
+       }
+
+       squashfs_closedir(dir);
+
+       return scan_res;
+}
+
+
+int dir_scan(char *parent_name, unsigned int start_block, unsigned int offset,
+       struct pathnames *paths)
+{
+       unsigned int type;
+       int scan_res = TRUE;
+       char *name;
+       struct pathnames *new;
+       struct inode *i;
+       struct dir *dir = s_ops->opendir(start_block, offset, &i);
+
+       if(dir == NULL) {
+               EXIT_UNSQUASH_IGNORE("dir_scan: failed to read directory %s\n",
+                       parent_name);
+               return FALSE;
+       }
+
+       if((lsonly || info) && (!concise || dir->dir_count ==0))
+               print_filename(parent_name, i);
+
+       if(!lsonly) {
+               /*
+                * Make directory with default User rwx permissions rather than
+                * the permissions from the filesystem, as these may not have
+                * write/execute permission.  These are fixed up later in
+                * set_attributes().
+                */
+               int res = mkdir(parent_name, S_IRUSR|S_IWUSR|S_IXUSR);
+               if(res == -1) {
+                       /*
+                        * Skip directory if mkdir fails, unless we're
+                        * forcing and the error is -EEXIST
+                        */
+                       if(!force || errno != EEXIST) {
+                               EXIT_UNSQUASH_IGNORE("dir_scan: failed to make directory %s, "
+                                       "because %s\n", parent_name,
+                                       strerror(errno));
+                               squashfs_closedir(dir);
+                               return FALSE;
+                       } 
+
+                       /*
+                        * Try to change permissions of existing directory so
+                        * that we can write to it
+                        */
+                       res = chmod(parent_name, S_IRUSR|S_IWUSR|S_IXUSR);
+                       if (res == -1) {
+                               EXIT_UNSQUASH_IGNORE("dir_scan: failed to change permissions "
+                                       "for directory %s, because %s\n",
+                                       parent_name, strerror(errno));
+                               squashfs_closedir(dir);
+                               return FALSE;
+                       }
+               }
+       }
+
+       while(squashfs_readdir(dir, &name, &start_block, &offset, &type)) {
+               char *pathname;
+               int res;
+
+               TRACE("dir_scan: name %s, start_block %d, offset %d, type %d\n",
+                       name, start_block, offset, type);
+
+
+               if(!matches(paths, name, &new))
+                       continue;
+
+               res = asprintf(&pathname, "%s/%s", parent_name, name);
+               if(res == -1)
+                       EXIT_UNSQUASH("asprintf failed in dir_scan\n");
+
+               if(type == SQUASHFS_DIR_TYPE) {
+                       res = dir_scan(pathname, start_block, offset, new);
+                       if(res == FALSE)
+                               scan_res = FALSE;
+                       free(pathname);
+               } else if(new == NULL) {
+                       update_info(pathname);
+
+                       i = s_ops->read_inode(start_block, offset);
+
+                       if(lsonly || info)
+                               print_filename(pathname, i);
+
+                       if(!lsonly) {
+                               res = create_inode(pathname, i);
+                               if(res == FALSE)
+                                       scan_res = FALSE;
+                       }
+
+                       if(i->type == SQUASHFS_SYMLINK_TYPE ||
+                                       i->type == SQUASHFS_LSYMLINK_TYPE)
+                               free(i->symlink);
+               } else
+                       free(pathname);
+
+               free_subdir(new);
+       }
+
+       if(!lsonly)
+               queue_dir(parent_name, dir);
+
+       squashfs_closedir(dir);
+       dir_count ++;
+
+       return scan_res;
+}
+
+
+void squashfs_stat(char *source)
+{
+       time_t mkfs_time = (time_t) sBlk.s.mkfs_time;
+       struct tm *t = use_localtime ? localtime(&mkfs_time) : gmtime(&mkfs_time);
+       char *mkfs_str = asctime(t);
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+       printf("Found a valid %sSQUASHFS %d:%d superblock on %s.\n",
+               sBlk.s.s_major == 4 ? "" : swap ? "little endian " :
+               "big endian ", sBlk.s.s_major, sBlk.s.s_minor, source);
+#else
+       printf("Found a valid %sSQUASHFS %d:%d superblock on %s.\n",
+               sBlk.s.s_major == 4 ? "" : swap ? "big endian " :
+               "little endian ", sBlk.s.s_major, sBlk.s.s_minor, source);
+#endif
+
+       printf("Creation or last append time %s", mkfs_str ? mkfs_str :
+               "failed to get time\n");
+       printf("Filesystem size %llu bytes (%.2f Kbytes / %.2f Mbytes)\n",
+               sBlk.s.bytes_used, sBlk.s.bytes_used / 1024.0,
+               sBlk.s.bytes_used / (1024.0 * 1024.0));
+
+       if(sBlk.s.s_major == 4) {
+               printf("Compression %s\n", comp->name);
+
+               if(SQUASHFS_COMP_OPTS(sBlk.s.flags)) {
+                       char buffer[SQUASHFS_METADATA_SIZE] __attribute__ ((aligned));
+                       int bytes;
+
+                       if(!comp->supported)
+                               printf("\tCould not display compressor options, because %s compression is not supported\n",
+                                               comp->name);
+                       else {
+                               bytes = read_block(fd, sizeof(sBlk.s), NULL, 0, buffer);
+                               if(bytes == 0) {
+                                       ERROR("Failed to read compressor options\n");
+                                       return;
+                               }
+
+                               compressor_display_options(comp, buffer, bytes);
+                       }
+               }
+       }
+
+       printf("Block size %d\n", sBlk.s.block_size);
+       printf("Filesystem is %sexportable via NFS\n",
+               SQUASHFS_EXPORTABLE(sBlk.s.flags) ? "" : "not ");
+       printf("Inodes are %scompressed\n",
+               SQUASHFS_UNCOMPRESSED_INODES(sBlk.s.flags) ? "un" : "");
+       printf("Data is %scompressed\n",
+               SQUASHFS_UNCOMPRESSED_DATA(sBlk.s.flags) ? "un" : "");
+
+       if(sBlk.s.s_major >= 4)
+               printf("Uids/Gids (Id table) are %scompressed\n",
+                       SQUASHFS_UNCOMPRESSED_INODES(sBlk.s.flags) ||
+                       SQUASHFS_UNCOMPRESSED_IDS(sBlk.s.flags) ? "un" : "");
+
+       if(sBlk.s.s_major > 1) {
+               if(SQUASHFS_NO_FRAGMENTS(sBlk.s.flags))
+                       printf("Fragments are not stored\n");
+               else {
+                       printf("Fragments are %scompressed\n",
+                               SQUASHFS_UNCOMPRESSED_FRAGMENTS(sBlk.s.flags) ?
+                               "un" : "");
+                       printf("Always-use-fragments option is %sspecified\n",
+                               SQUASHFS_ALWAYS_FRAGMENTS(sBlk.s.flags) ? "" :
+                               "not ");
+               }
+       }
+
+       if(sBlk.s.s_major == 4) {
+               if(SQUASHFS_NO_XATTRS(sBlk.s.flags))
+                       printf("Xattrs are not stored\n");
+               else
+                       printf("Xattrs are %scompressed\n",
+                               SQUASHFS_UNCOMPRESSED_XATTRS(sBlk.s.flags) ?
+                               "un" : "");
+       }
+
+       if(sBlk.s.s_major < 4)
+                       printf("Check data is %spresent in the filesystem\n",
+                               SQUASHFS_CHECK_DATA(sBlk.s.flags) ? "" :
+                               "not ");
+
+       if(sBlk.s.s_major > 1)
+               printf("Duplicates are %sremoved\n",
+                       SQUASHFS_DUPLICATES(sBlk.s.flags) ? "" : "not ");
+       else
+               printf("Duplicates are removed\n");
+
+       if(sBlk.s.s_major > 1)
+               printf("Number of fragments %d\n", sBlk.s.fragments);
+
+       printf("Number of inodes %d\n", sBlk.s.inodes);
+
+       if(sBlk.s.s_major == 4)
+               printf("Number of ids %d\n", sBlk.s.no_ids);
+       else {
+               printf("Number of uids %d\n", sBlk.no_uids);
+               printf("Number of gids %d\n", sBlk.no_guids);
+       }
+
+       TRACE("sBlk.s.inode_table_start 0x%llx\n", sBlk.s.inode_table_start);
+       TRACE("sBlk.s.directory_table_start 0x%llx\n",
+               sBlk.s.directory_table_start);
+
+       if(sBlk.s.s_major > 1)
+               TRACE("sBlk.s.fragment_table_start 0x%llx\n\n",
+                       sBlk.s.fragment_table_start);
+
+       if(sBlk.s.s_major > 2)
+               TRACE("sBlk.s.lookup_table_start 0x%llx\n\n",
+                       sBlk.s.lookup_table_start);
+
+       if(sBlk.s.s_major == 4) {
+               TRACE("sBlk.s.id_table_start 0x%llx\n", sBlk.s.id_table_start);
+               TRACE("sBlk.s.xattr_id_table_start 0x%llx\n",
+                       sBlk.s.xattr_id_table_start);
+       } else {
+               TRACE("sBlk.uid_start 0x%llx\n", sBlk.uid_start);
+               TRACE("sBlk.guid_start 0x%llx\n", sBlk.guid_start);
+       }
+}
+
+
+int check_compression(struct compressor *comp)
+{
+       int res, bytes = 0;
+       char buffer[SQUASHFS_METADATA_SIZE] __attribute__ ((aligned));
+
+       if(!comp->supported) {
+               ERROR("Filesystem uses %s compression, this is "
+                       "unsupported by this version\n", comp->name);
+               ERROR("Decompressors available:\n");
+               display_compressors("", "");
+               return FALSE;
+       }
+
+       /*
+        * Read compression options from disk if present, and pass to
+        * the compressor to ensure we know how to decompress a filesystem
+        * compressed with these compression options.
+        *
+        * Note, even if there is no compression options we still call the
+        * compressor because some compression options may be mandatory
+        * for some compressors.
+        */
+       if(SQUASHFS_COMP_OPTS(sBlk.s.flags)) {
+               bytes = read_block(fd, sizeof(sBlk.s), NULL, 0, buffer);
+               if(bytes == 0) {
+                       ERROR("Failed to read compressor options\n");
+                       return FALSE;
+               }
+       }
+
+       res = compressor_check_options(comp, sBlk.s.block_size, buffer, bytes);
+
+       return res != -1;
+}
+
+
+int read_super(char *source)
+{
+       squashfs_super_block_3 sBlk_3;
+       struct squashfs_super_block sBlk_4;
+
+       /*
+        * Try to read a Squashfs 4 superblock
+        */
+       read_fs_bytes(fd, SQUASHFS_START, sizeof(struct squashfs_super_block),
+               &sBlk_4);
+       swap = sBlk_4.s_magic != SQUASHFS_MAGIC;
+       SQUASHFS_INSWAP_SUPER_BLOCK(&sBlk_4);
+
+       if(sBlk_4.s_magic == SQUASHFS_MAGIC && sBlk_4.s_major == 4 &&
+                       sBlk_4.s_minor == 0) {
+               read_filesystem_tables = read_filesystem_tables_4;
+               memcpy(&sBlk, &sBlk_4, sizeof(sBlk_4));
+
+               /*
+                * Check the compression type
+                */
+               comp = lookup_compressor_id(sBlk.s.compression);
+               return TRUE;
+       }
+
+       /*
+        * Not a Squashfs 4 superblock, try to read a squashfs 3 superblock
+        * (compatible with 1 and 2 filesystems)
+        */
+       read_fs_bytes(fd, SQUASHFS_START, sizeof(squashfs_super_block_3),
+               &sBlk_3);
+
+       /*
+        * Check it is a SQUASHFS superblock
+        */
+       swap = 0;
+       if(sBlk_3.s_magic != SQUASHFS_MAGIC) {
+               if(sBlk_3.s_magic == SQUASHFS_MAGIC_SWAP) {
+                       squashfs_super_block_3 sblk;
+                       ERROR("Reading a different endian SQUASHFS filesystem "
+                               "on %s\n", source);
+                       SQUASHFS_SWAP_SUPER_BLOCK_3(&sblk, &sBlk_3);
+                       memcpy(&sBlk_3, &sblk, sizeof(squashfs_super_block_3));
+                       swap = 1;
+               } else  {
+                       ERROR("Can't find a SQUASHFS superblock on %s\n",
+                               source);
+                       goto failed_mount;
+               }
+       }
+
+       sBlk.s.s_magic = sBlk_3.s_magic;
+       sBlk.s.inodes = sBlk_3.inodes;
+       sBlk.s.mkfs_time = sBlk_3.mkfs_time;
+       sBlk.s.block_size = sBlk_3.block_size;
+       sBlk.s.fragments = sBlk_3.fragments;
+       sBlk.s.block_log = sBlk_3.block_log;
+       sBlk.s.flags = sBlk_3.flags;
+       sBlk.s.s_major = sBlk_3.s_major;
+       sBlk.s.s_minor = sBlk_3.s_minor;
+       sBlk.s.root_inode = sBlk_3.root_inode;
+       sBlk.s.bytes_used = sBlk_3.bytes_used;
+       sBlk.s.inode_table_start = sBlk_3.inode_table_start;
+       sBlk.s.directory_table_start = sBlk_3.directory_table_start;
+       sBlk.s.fragment_table_start = sBlk_3.fragment_table_start;
+       sBlk.s.lookup_table_start = sBlk_3.lookup_table_start;
+       sBlk.no_uids = sBlk_3.no_uids;
+       sBlk.no_guids = sBlk_3.no_guids;
+       sBlk.uid_start = sBlk_3.uid_start;
+       sBlk.guid_start = sBlk_3.guid_start;
+       sBlk.s.xattr_id_table_start = SQUASHFS_INVALID_BLK;
+
+       /* Check the MAJOR & MINOR versions */
+       if(sBlk.s.s_major == 1 || sBlk.s.s_major == 2) {
+               sBlk.s.bytes_used = sBlk_3.bytes_used_2;
+               sBlk.uid_start = sBlk_3.uid_start_2;
+               sBlk.guid_start = sBlk_3.guid_start_2;
+               sBlk.s.inode_table_start = sBlk_3.inode_table_start_2;
+               sBlk.s.directory_table_start = sBlk_3.directory_table_start_2;
+               
+               if(sBlk.s.s_major == 1) {
+                       sBlk.s.block_size = sBlk_3.block_size_1;
+                       sBlk.s.fragment_table_start = sBlk.uid_start;
+                       read_filesystem_tables = read_filesystem_tables_1;
+               } else {
+                       sBlk.s.fragment_table_start =
+                               sBlk_3.fragment_table_start_2;
+                       read_filesystem_tables = read_filesystem_tables_2;
+               }
+       } else if(sBlk.s.s_major == 3) {
+               read_filesystem_tables = read_filesystem_tables_3;
+       } else {
+               ERROR("Filesystem on %s is (%d:%d), ", source, sBlk.s.s_major,
+                       sBlk.s.s_minor);
+               ERROR("which is a later filesystem version than I support!\n");
+               goto failed_mount;
+       }
+
+       /*
+        * 1.x, 2.x and 3.x filesystems use gzip compression.
+        */
+       comp = lookup_compressor("gzip");
+       return TRUE;
+
+failed_mount:
+       return FALSE;
+}
+
+
+struct pathname *process_extract_files(struct pathname *path, char *filename)
+{
+       FILE *fd;
+       char buffer[MAX_LINE + 1]; /* overflow safe */
+       char *name;
+
+       fd = fopen(filename, "r");
+       if(fd == NULL)
+               EXIT_UNSQUASH("Failed to open extract file \"%s\" because %s\n",
+                       filename, strerror(errno));
+
+       while(fgets(name = buffer, MAX_LINE + 1, fd) != NULL) {
+               int len = strlen(name);
+
+               if(len == MAX_LINE && name[len - 1] != '\n')
+                       /* line too large */
+                       EXIT_UNSQUASH("Line too long when reading "
+                               "extract file \"%s\", larger than %d "
+                               "bytes\n", filename, MAX_LINE);
+
+               /*
+                * Remove '\n' terminator if it exists (the last line
+                * in the file may not be '\n' terminated)
+                */
+               if(len && name[len - 1] == '\n')
+                       name[len - 1] = '\0';
+
+               /* Skip any leading whitespace */
+               while(isspace(*name))
+                       name ++;
+
+               /* if comment line, skip */
+               if(*name == '#')
+                       continue;
+
+               /* check for initial backslash, to accommodate
+                * filenames with leading space or leading # character
+                */
+               if(*name == '\\')
+                       name ++;
+
+               /* if line is now empty after skipping characters, skip it */
+               if(*name == '\0')
+                       continue;
+
+               path = add_path(path, name, name);
+       }
+
+       if(ferror(fd))
+               EXIT_UNSQUASH("Reading extract file \"%s\" failed because %s\n",
+                       filename, strerror(errno));
+
+       fclose(fd);
+       return path;
+}
+               
+
+/*
+ * reader thread.  This thread processes read requests queued by the
+ * cache_get() routine.
+ */
+void *reader(void *arg)
+{
+       while(1) {
+               struct cache_entry *entry = queue_get(to_reader);
+               int res = read_fs_bytes(fd, entry->block,
+                       SQUASHFS_COMPRESSED_SIZE_BLOCK(entry->size),
+                       entry->data);
+
+               if(res && SQUASHFS_COMPRESSED_BLOCK(entry->size))
+                       /*
+                        * queue successfully read block to the inflate
+                        * thread(s) for further processing
+                        */
+                       queue_put(to_inflate, entry);
+               else
+                       /*
+                        * block has either been successfully read and is
+                        * uncompressed, or an error has occurred, clear pending
+                        * flag, set error appropriately, and wake up any
+                        * threads waiting on this buffer
+                        */
+                       cache_block_ready(entry, !res);
+       }
+}
+
+
+/*
+ * writer thread.  This processes file write requests queued by the
+ * write_file() routine.
+ */
+void *writer(void *arg)
+{
+       int i;
+       long failed = FALSE;
+
+       while(1) {
+               struct squashfs_file *file = queue_get(to_writer);
+               int file_fd;
+               long long hole = 0;
+               int res;
+
+               if(file == NULL) {
+                       queue_put(from_writer, (void *) failed);
+                       continue;
+               } else if(file->fd == -1) {
+                       /* write attributes for directory file->pathname */
+                       res = set_attributes(file->pathname, file->mode, file->uid,
+                               file->gid, file->time, file->xattr, TRUE);
+                       if(res == FALSE)
+                               failed = TRUE;
+                       free(file->pathname);
+                       free(file);
+                       continue;
+               }
+
+               TRACE("writer: regular file, blocks %d\n", file->blocks);
+
+               file_fd = file->fd;
+
+               for(i = 0; i < file->blocks; i++, cur_blocks ++) {
+                       struct file_entry *block = queue_get(to_writer);
+
+                       if(block->buffer == 0) { /* sparse file */
+                               hole += block->size;
+                               free(block);
+                               continue;
+                       }
+
+                       cache_block_wait(block->buffer);
+
+                       if(block->buffer->error) {
+                               EXIT_UNSQUASH_IGNORE("writer: failed to read/uncompress file %s\n", file->pathname);
+                               failed = TRUE;
+                       }
+
+                       if(failed)
+                               continue;
+
+                       res = write_block(file_fd, block->buffer->data +
+                               block->offset, block->size, hole, file->sparse);
+
+                       if(res == FALSE) {
+                               EXIT_UNSQUASH_IGNORE("writer: failed to write file %s\n", file->pathname);
+                               failed = TRUE;
+                       }
+
+                       hole = 0;
+                       cache_block_put(block->buffer);
+                       free(block);
+               }
+
+               if(hole && failed == FALSE) {
+                       /*
+                        * corner case for hole extending to end of file
+                        */
+                       if(file->sparse == FALSE ||
+                                       lseek(file_fd, hole, SEEK_CUR) == -1) {
+                               /*
+                                * for files which we don't want to write
+                                * sparsely, or for broken lseeks which cannot
+                                * seek beyond end of file, write_block will do
+                                * the right thing
+                                */
+                               hole --;
+                               if(write_block(file_fd, "\0", 1, hole,
+                                               file->sparse) == FALSE) {
+                                       EXIT_UNSQUASH_IGNORE("writer: failed to write sparse "
+                                               "data block for file %s\n", file->pathname);
+                                       failed = TRUE;
+                               }
+                       } else if(ftruncate(file_fd, file->file_size) == -1) {
+                               EXIT_UNSQUASH_IGNORE("writer: failed to write sparse data "
+                                       "block for file %s\n", file->pathname);
+                               failed = TRUE;
+                       }
+               }
+
+               close_wake(file_fd);
+               if(failed == FALSE) {
+                       res = set_attributes(file->pathname, file->mode, file->uid,
+                               file->gid, file->time, file->xattr, force);
+                       if(res == FALSE)
+                               failed = TRUE;
+               } else
+                       unlink(file->pathname);
+               free(file->pathname);
+               free(file);
+
+       }
+}
+
+
+/*
+ * decompress thread.  This decompresses buffers queued by the read thread
+ */
+void *inflator(void *arg)
+{
+       char *tmp = malloc(block_size);
+
+       if(tmp == NULL)
+               EXIT_UNSQUASH("inflator: Failed to allocate block buffer\n");
+
+       while(1) {
+               struct cache_entry *entry = queue_get(to_inflate);
+               int error, res;
+
+               res = compressor_uncompress(comp, tmp, entry->data,
+                       SQUASHFS_COMPRESSED_SIZE_BLOCK(entry->size), block_size,
+                       &error);
+
+               if(res == -1)
+                       ERROR("%s uncompress failed with error code %d\n",
+                               comp->name, error);
+               else
+                       memcpy(entry->data, tmp, res);
+
+               /*
+                * block has been either successfully decompressed, or an error
+                * occurred, clear pending flag, set error appropriately and
+                * wake up any threads waiting on this block
+                */ 
+               cache_block_ready(entry, res == -1);
+       }
+}
+
+
+void *progress_thread(void *arg)
+{
+       struct timespec requested_time, remaining;
+       struct itimerval itimerval;
+       struct winsize winsize;
+
+       if(ioctl(1, TIOCGWINSZ, &winsize) == -1) {
+               if(isatty(STDOUT_FILENO))
+                       ERROR("TIOCGWINSZ ioctl failed, defaulting to 80 "
+                               "columns\n");
+               columns = 80;
+       } else
+               columns = winsize.ws_col;
+       signal(SIGWINCH, sigwinch_handler);
+       signal(SIGALRM, sigalrm_handler);
+
+       itimerval.it_value.tv_sec = 0;
+       itimerval.it_value.tv_usec = 250000;
+       itimerval.it_interval.tv_sec = 0;
+       itimerval.it_interval.tv_usec = 250000;
+       setitimer(ITIMER_REAL, &itimerval, NULL);
+
+       requested_time.tv_sec = 0;
+       requested_time.tv_nsec = 250000000;
+
+       while(1) {
+               int res = nanosleep(&requested_time, &remaining);
+
+               if(res == -1 && errno != EINTR)
+                       EXIT_UNSQUASH("nanosleep failed in progress thread\n");
+
+               if(progress_enabled) {
+                       pthread_mutex_lock(&screen_mutex);
+                       progress_bar(sym_count + dev_count +
+                               fifo_count + cur_blocks, total_inodes -
+                               total_files + total_blocks, columns);
+                       pthread_mutex_unlock(&screen_mutex);
+               }
+       }
+}
+
+
+void initialise_threads(int fragment_buffer_size, int data_buffer_size)
+{
+       struct rlimit rlim;
+       int i, max_files, res;
+       sigset_t sigmask, old_mask;
+
+       /* block SIGQUIT and SIGHUP, these are handled by the info thread */
+       sigemptyset(&sigmask);
+       sigaddset(&sigmask, SIGQUIT);
+       sigaddset(&sigmask, SIGHUP);
+       if(pthread_sigmask(SIG_BLOCK, &sigmask, NULL) != 0)
+               EXIT_UNSQUASH("Failed to set signal mask in initialise_threads"
+                       "\n");
+
+       /*
+        * temporarily block these signals so the created sub-threads will
+        * ignore them, ensuring the main thread handles them
+        */
+       sigemptyset(&sigmask);
+       sigaddset(&sigmask, SIGINT);
+       sigaddset(&sigmask, SIGTERM);
+       if(pthread_sigmask(SIG_BLOCK, &sigmask, &old_mask) != 0)
+               EXIT_UNSQUASH("Failed to set signal mask in initialise_threads"
+                       "\n");
+
+       if(processors == -1) {
+#ifndef linux
+               int mib[2];
+               size_t len = sizeof(processors);
+
+               mib[0] = CTL_HW;
+#ifdef HW_AVAILCPU
+               mib[1] = HW_AVAILCPU;
+#else
+               mib[1] = HW_NCPU;
+#endif
+
+               if(sysctl(mib, 2, &processors, &len, NULL, 0) == -1) {
+                       ERROR("Failed to get number of available processors.  "
+                               "Defaulting to 1\n");
+                       processors = 1;
+               }
+#else
+               processors = sysconf(_SC_NPROCESSORS_ONLN);
+#endif
+       }
+
+       if(add_overflow(processors, 3) ||
+                       multiply_overflow(processors + 3, sizeof(pthread_t)))
+               EXIT_UNSQUASH("Processors too large\n");
+
+       thread = malloc((3 + processors) * sizeof(pthread_t));
+       if(thread == NULL)
+               EXIT_UNSQUASH("Out of memory allocating thread descriptors\n");
+       inflator_thread = &thread[3];
+
+       /*
+        * dimensioning the to_reader and to_inflate queues.  The size of
+        * these queues is directly related to the amount of block
+        * read-ahead possible.  To_reader queues block read requests to
+        * the reader thread and to_inflate queues block decompression
+        * requests to the inflate thread(s) (once the block has been read by
+        * the reader thread).  The amount of read-ahead is determined by
+        * the combined size of the data_block and fragment caches which
+        * determine the total number of blocks which can be "in flight"
+        * at any one time (either being read or being decompressed)
+        *
+        * The maximum file open limit, however, affects the read-ahead
+        * possible, in that for normal sizes of the fragment and data block
+        * caches, where the incoming files have few data blocks or one fragment
+        * only, the file open limit is likely to be reached before the
+        * caches are full.  This means the worst case sizing of the combined
+        * sizes of the caches is unlikely to ever be necessary.  However, is is
+        * obvious read-ahead up to the data block cache size is always possible
+        * irrespective of the file open limit, because a single file could
+        * contain that number of blocks.
+        *
+        * Choosing the size as "file open limit + data block cache size" seems
+        * to be a reasonable estimate.  We can reasonably assume the maximum
+        * likely read-ahead possible is data block cache size + one fragment
+        * per open file.
+        *
+        * dimensioning the to_writer queue.  The size of this queue is
+        * directly related to the amount of block read-ahead possible.
+        * However, unlike the to_reader and to_inflate queues, this is
+        * complicated by the fact the to_writer queue not only contains
+        * entries for fragments and data_blocks but it also contains
+        * file entries, one per open file in the read-ahead.
+        *
+        * Choosing the size as "2 * (file open limit) +
+        * data block cache size" seems to be a reasonable estimate.
+        * We can reasonably assume the maximum likely read-ahead possible
+        * is data block cache size + one fragment per open file, and then
+        * we will have a file_entry for each open file.
+        */
+       res = getrlimit(RLIMIT_NOFILE, &rlim);
+       if (res == -1) {
+               ERROR("failed to get open file limit!  Defaulting to 1\n");
+               rlim.rlim_cur = 1;
+       }
+
+       if (rlim.rlim_cur != RLIM_INFINITY) {
+               /*
+                * leave OPEN_FILE_MARGIN free (rlim_cur includes fds used by
+                * stdin, stdout, stderr and filesystem fd
+                */
+               if (rlim.rlim_cur <= OPEN_FILE_MARGIN)
+                       /* no margin, use minimum possible */
+                       max_files = 1;
+               else
+                       max_files = rlim.rlim_cur - OPEN_FILE_MARGIN;
+       } else
+               max_files = -1;
+
+       /* set amount of available files for use by open_wait and close_wake */
+       open_init(max_files);
+
+       /*
+        * allocate to_reader, to_inflate and to_writer queues.  Set based on
+        * open file limit and cache size, unless open file limit is unlimited,
+        * in which case set purely based on cache limits
+        *
+        * In doing so, check that the user supplied values do not overflow
+        * a signed int
+        */
+       if (max_files != -1) {
+               if(add_overflow(data_buffer_size, max_files) ||
+                               add_overflow(data_buffer_size, max_files * 2))
+                       EXIT_UNSQUASH("Data queue size is too large\n");
+
+               to_reader = queue_init(max_files + data_buffer_size);
+               to_inflate = queue_init(max_files + data_buffer_size);
+               to_writer = queue_init(max_files * 2 + data_buffer_size);
+       } else {
+               int all_buffers_size;
+
+               if(add_overflow(fragment_buffer_size, data_buffer_size))
+                       EXIT_UNSQUASH("Data and fragment queues combined are"
+                                                       " too large\n");
+
+               all_buffers_size = fragment_buffer_size + data_buffer_size;
+
+               if(add_overflow(all_buffers_size, all_buffers_size))
+                       EXIT_UNSQUASH("Data and fragment queues combined are"
+                                                       " too large\n");
+
+               to_reader = queue_init(all_buffers_size);
+               to_inflate = queue_init(all_buffers_size);
+               to_writer = queue_init(all_buffers_size * 2);
+       }
+
+       from_writer = queue_init(1);
+
+       fragment_cache = cache_init(block_size, fragment_buffer_size);
+       data_cache = cache_init(block_size, data_buffer_size);
+       pthread_create(&thread[0], NULL, reader, NULL);
+       pthread_create(&thread[1], NULL, writer, NULL);
+       pthread_create(&thread[2], NULL, progress_thread, NULL);
+       init_info();
+       pthread_mutex_init(&fragment_mutex, NULL);
+
+       for(i = 0; i < processors; i++) {
+               if(pthread_create(&inflator_thread[i], NULL, inflator, NULL) !=
+                                0)
+                       EXIT_UNSQUASH("Failed to create thread\n");
+       }
+
+       if(pthread_sigmask(SIG_SETMASK, &old_mask, NULL) != 0)
+               EXIT_UNSQUASH("Failed to set signal mask in initialise_threads"
+                       "\n");
+}
+
+
+void enable_progress_bar()
+{
+       pthread_mutex_lock(&screen_mutex);
+       progress_enabled = progress;
+       pthread_mutex_unlock(&screen_mutex);
+}
+
+
+void disable_progress_bar()
+{
+       pthread_mutex_lock(&screen_mutex);
+       if(progress_enabled) {
+               progress_bar(sym_count + dev_count + fifo_count + cur_blocks,
+                       total_inodes - total_files + total_blocks, columns);
+               printf("\n");
+       }
+       progress_enabled = FALSE;
+       pthread_mutex_unlock(&screen_mutex);
+}
+
+
+void progressbar_error(char *fmt, ...)
+{
+       va_list ap;
+
+       pthread_mutex_lock(&screen_mutex);
+
+       if(progress_enabled)
+               fprintf(stderr, "\n");
+
+       va_start(ap, fmt);
+       vfprintf(stderr, fmt, ap);
+       va_end(ap);
+
+       pthread_mutex_unlock(&screen_mutex);
+}
+
+
+void progressbar_info(char *fmt, ...)
+{
+       va_list ap;
+
+       pthread_mutex_lock(&screen_mutex);
+
+       if(progress_enabled)
+               printf("\n");
+
+       va_start(ap, fmt);
+       vprintf(fmt, ap);
+       va_end(ap);
+
+       pthread_mutex_unlock(&screen_mutex);
+}
+static int get_max_digits(long long max)
+{
+    int digits = 0;
+    
+    while (max > 10) {
+        max /= 10;
+        digits++;
+    }
+
+    return digits;
+}
+
+void progress_bar(long long current, long long max, int columns)
+{
+       char rotate_list[] = { '|', '/', '-', '\\' };
+       int max_digits, used, hashes, spaces;
+       static int tty = -1;
+
+       if(max == 0)
+               return;
+
+       //max_digits = floor(log10(max)) + 1;
+       max_digits = get_max_digits(max) + 1;
+       used = max_digits * 2 + 11;
+       hashes = (current * (columns - used)) / max;
+       spaces = columns - used - hashes;
+
+       if((current > max) || (columns - used < 0))
+               return;
+
+       if(tty == -1)
+               tty = isatty(STDOUT_FILENO);
+       if(!tty) {
+               static long long previous = -1;
+
+               /*
+                * Updating much more frequently than this results in huge
+                * log files.
+                */
+               if((current % 100) != 0 && current != max)
+                       return;
+               /* Don't update just to rotate the spinner. */
+               if(current == previous)
+                       return;
+               previous = current;
+       }
+
+       printf("\r[");
+
+       while (hashes --)
+               putchar('=');
+
+       putchar(rotate_list[rotate]);
+
+       while(spaces --)
+               putchar(' ');
+
+       printf("] %*lld/%*lld", max_digits, current, max_digits, max);
+       printf(" %3lld%%", current * 100 / max);
+       fflush(stdout);
+}
+
+
+int multiply_overflowll(long long a, int multiplier)
+{
+       return (LLONG_MAX / multiplier) < a;
+}
+
+
+int parse_numberll(char *start, long long *res, int size)
+{
+       char *end;
+       long long number;
+
+       errno = 0; /* To distinguish success/failure after call */
+
+       number = strtoll(start, &end, 10);
+
+       /*
+        * check for strtoll underflow or overflow in conversion, and other
+        * errors.
+        */
+       if((errno == ERANGE && (number == LLONG_MIN || number == LLONG_MAX)) ||
+                       (errno != 0 && number == 0))
+               return 0;
+
+       /* reject negative numbers as invalid */
+       if(number < 0)
+               return 0;
+
+       if(size) {
+               /*
+                * Check for multiplier and trailing junk.
+                * But first check that a number exists before the
+                * multiplier
+                */
+               if(end == start)
+                       return 0;
+
+               switch(end[0]) {
+               case 'g':
+               case 'G':
+                       if(multiply_overflowll(number, 1073741824))
+                               return 0;
+                       number *= 1073741824;
+
+                       if(end[1] != '\0')
+                               /* trailing junk after multiplier, but
+                                * allow it to be "bytes" */
+                               if(strcmp(end + 1, "bytes"))
+                                       return 0;
+
+                       break;
+               case 'm':
+               case 'M':
+                       if(multiply_overflowll(number, 1048576))
+                               return 0;
+                       number *= 1048576;
+
+                       if(end[1] != '\0')
+                               /* trailing junk after multiplier, but
+                                * allow it to be "bytes" */
+                               if(strcmp(end + 1, "bytes"))
+                                       return 0;
+
+                       break;
+               case 'k':
+               case 'K':
+                       if(multiply_overflowll(number, 1024))
+                               return 0;
+                       number *= 1024;
+
+                       if(end[1] != '\0')
+                               /* trailing junk after multiplier, but
+                                * allow it to be "bytes" */
+                               if(strcmp(end + 1, "bytes"))
+                                       return 0;
+
+                       break;
+               case '\0':
+                       break;
+               default:
+                       /* trailing junk after number */
+                       return 0;
+               }
+       } else if(end[0] != '\0')
+               /* trailing junk after number */
+               return 0;
+
+       *res = number;
+       return 1;
+}
+
+
+int parse_number(char *start, int *res)
+{
+       long long number;
+
+       if(!parse_numberll(start, &number, 0))
+               return 0;
+
+       /* check if long result will overflow signed int */
+       if(number > INT_MAX)
+               return 0;
+
+       *res = (int) number;
+       return 1;
+}
+
+
+int ventoy_parse_disk_map(void)
+{
+    int len = 0;
+
+    debug("ventoy_parse_disk_map\n");
+
+    len = (int)lseek(fd, 0, SEEK_END);
+    lseek(fd, 0, SEEK_SET);
+
+    if (len < sizeof(fs_disk_map) + sizeof(fs_disk_region))
+    {
+        return 1;
+    }
+
+    read(fd, &g_fs_disk_map, sizeof(fs_disk_map));
+
+    debug("diskname=<%s> filesize=<%llu> region_num=<%u>\n", 
+          g_fs_disk_map.diskname, g_fs_disk_map.filesize, g_fs_region_num);
+
+    g_fs_region_num = (len - sizeof(fs_disk_map)) / sizeof(fs_disk_region);
+    g_fs_region_list = malloc(g_fs_region_num * sizeof(fs_disk_region));
+    read(fd, g_fs_region_list, g_fs_region_num * sizeof(fs_disk_region));
+
+    close(fd);    
+    
+    fd = open(g_fs_disk_map.diskname, O_RDONLY);
+    debug("ventoy_parse_disk_map end fd=%d\n", fd);
+    
+    return 0;
+}
+
+
+#define VERSION() \
+       printf("unsquashfs version 4.4 (2019/08/29)\n");\
+       printf("copyright (C) 2019 Phillip Lougher "\
+               "<phillip@squashfs.org.uk>\n\n");\
+       printf("This program is free software; you can redistribute it and/or"\
+               "\n");\
+       printf("modify it under the terms of the GNU General Public License"\
+               "\n");\
+       printf("as published by the Free Software Foundation; either version "\
+               "2,\n");\
+       printf("or (at your option) any later version.\n\n");\
+       printf("This program is distributed in the hope that it will be "\
+               "useful,\n");\
+       printf("but WITHOUT ANY WARRANTY; without even the implied warranty of"\
+               "\n");\
+       printf("MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the"\
+               "\n");\
+       printf("GNU General Public License for more details.\n");
+int main(int argc, char *argv[])
+{
+       char *dest = "squashfs-root";
+       int i, stat_sys = FALSE, version = FALSE, mkfs_time_opt = FALSE;
+       int n;
+       struct pathnames *paths = NULL;
+       struct pathname *path = NULL;
+       int fragment_buffer_size = FRAGMENT_BUFFER_DEFAULT;
+       int data_buffer_size = DATA_BUFFER_DEFAULT;
+       long res;
+       int exit_code = 0;
+
+       pthread_mutex_init(&screen_mutex, NULL);
+       root_process = geteuid() == 0;
+       if(root_process)
+               umask(0);
+       
+       for(i = 1; i < argc; i++) {
+               if(*argv[i] != '-')
+                       break;
+               if(strcmp(argv[i], "-UTC") == 0)
+                       use_localtime = FALSE;
+        else if (strcmp(argv[i], "-t") == 0)
+            return 0;
+        else if (strcmp(argv[i], "-v") == 0)
+            verbose = 1;
+               else if(strcmp(argv[i], "-strict-errors") == 0 ||
+                               strcmp(argv[i], "-st") == 0)
+                       strict_errors = TRUE;
+               else if(strcmp(argv[i], "-ignore-errors") == 0 ||
+                               strcmp(argv[i], "-ig") == 0)
+                       ignore_errors = TRUE;
+               else if(strcmp(argv[i], "-quiet") == 0 ||
+                               strcmp(argv[i], "-q") == 0)
+                       quiet = TRUE;
+               else if(strcmp(argv[i], "-version") == 0 ||
+                               strcmp(argv[i], "-v") == 0) {
+                       VERSION();
+                       version = TRUE;
+               } else if(strcmp(argv[i], "-info") == 0 ||
+                               strcmp(argv[i], "-i") == 0)
+                       info = TRUE;
+               else if(strcmp(argv[i], "-ls") == 0 ||
+                               strcmp(argv[i], "-l") == 0)
+                       lsonly = TRUE;
+               else if(strcmp(argv[i], "-lc") == 0) {
+                       lsonly = TRUE;
+                       concise = TRUE;
+               } else if(strcmp(argv[i], "-no-progress") == 0 ||
+                               strcmp(argv[i], "-n") == 0)
+                       progress = FALSE;
+               else if(strcmp(argv[i], "-no-xattrs") == 0 ||
+                               strcmp(argv[i], "-no") == 0)
+                       no_xattrs = TRUE;
+               else if(strcmp(argv[i], "-xattrs") == 0 ||
+                               strcmp(argv[i], "-x") == 0)
+                       no_xattrs = FALSE;
+               else if(strcmp(argv[i], "-user-xattrs") == 0 ||
+                               strcmp(argv[i], "-u") == 0) {
+                       user_xattrs = TRUE;
+                       no_xattrs = FALSE;
+               } else if(strcmp(argv[i], "-dest") == 0 ||
+                               strcmp(argv[i], "-d") == 0) {
+                       if(++i == argc) {
+                               fprintf(stderr, "%s: -dest missing filename\n",
+                                       argv[0]);
+                               exit(1);
+                       }
+                       dest = argv[i];
+               } else if(strcmp(argv[i], "-processors") == 0 ||
+                               strcmp(argv[i], "-p") == 0) {
+                       if((++i == argc) || 
+                                       !parse_number(argv[i],
+                                               &processors)) {
+                               ERROR("%s: -processors missing or invalid "
+                                       "processor number\n", argv[0]);
+                               exit(1);
+                       }
+                       if(processors < 1) {
+                               ERROR("%s: -processors should be 1 or larger\n",
+                                       argv[0]);
+                               exit(1);
+                       }
+               } else if(strcmp(argv[i], "-data-queue") == 0 ||
+                                        strcmp(argv[i], "-da") == 0) {
+                       if((++i == argc) ||
+                                       !parse_number(argv[i],
+                                               &data_buffer_size)) {
+                               ERROR("%s: -data-queue missing or invalid "
+                                       "queue size\n", argv[0]);
+                               exit(1);
+                       }
+                       if(data_buffer_size < 1) {
+                               ERROR("%s: -data-queue should be 1 Mbyte or "
+                                       "larger\n", argv[0]);
+                               exit(1);
+                       }
+               } else if(strcmp(argv[i], "-frag-queue") == 0 ||
+                                       strcmp(argv[i], "-fr") == 0) {
+                       if((++i == argc) ||
+                                       !parse_number(argv[i],
+                                               &fragment_buffer_size)) {
+                               ERROR("%s: -frag-queue missing or invalid "
+                                       "queue size\n", argv[0]);
+                               exit(1);
+                       }
+                       if(fragment_buffer_size < 1) {
+                               ERROR("%s: -frag-queue should be 1 Mbyte or "
+                                       "larger\n", argv[0]);
+                               exit(1);
+                       }
+               } else if(strcmp(argv[i], "-force") == 0 ||
+                               strcmp(argv[i], "-f") == 0)
+                       force = TRUE;
+               else if(strcmp(argv[i], "-stat") == 0 ||
+                               strcmp(argv[i], "-s") == 0)
+                       stat_sys = TRUE;
+               else if(strcmp(argv[i], "-mkfs-time") == 0 ||
+                               strcmp(argv[i], "-fstime") == 0)
+                       mkfs_time_opt = TRUE;
+               else if(strcmp(argv[i], "-lls") == 0 ||
+                               strcmp(argv[i], "-ll") == 0) {
+                       lsonly = TRUE;
+                       short_ls = FALSE;
+               } else if(strcmp(argv[i], "-llnumeric") == 0 ||
+                               strcmp(argv[i], "-lln") == 0) {
+                       lsonly = TRUE;
+                       short_ls = FALSE;
+                       numeric = TRUE;
+               } else if(strcmp(argv[i], "-llc") == 0) {
+                       lsonly = TRUE;
+                       short_ls = FALSE;
+                       concise = TRUE;
+               } else if(strcmp(argv[i], "-linfo") == 0 ||
+                               strcmp(argv[i], "-li") == 0) {
+                       info = TRUE;
+                       short_ls = FALSE;
+               } else if(strcmp(argv[i], "-ef") == 0 ||
+                               strcmp(argv[i], "-e") == 0) {
+                       if(++i == argc) {
+                               fprintf(stderr, "%s: -ef missing filename\n",
+                                       argv[0]);
+                               exit(1);
+                       }
+                       path = process_extract_files(path, argv[i]);
+               } else if(strcmp(argv[i], "-regex") == 0 ||
+                               strcmp(argv[i], "-r") == 0)
+                       use_regex = TRUE;
+               else if(strcmp(argv[i], "-offset") == 0 || strcmp(argv[i], "-o") == 0) {
+                       if((++i == argc) || !parse_numberll(argv[i], &start_offset, 1)) {
+                               ERROR("%s: %s missing or invalid offset size\n", argv[0], argv[i - 1]);
+                               exit(1);
+                       }
+               } else
+                       goto options;
+       }
+
+       if(lsonly || info)
+               progress = FALSE;
+
+       if(strict_errors && ignore_errors)
+               EXIT_UNSQUASH("Both -strict-errors and -ignore-errors should not be set\n");
+
+#ifdef SQUASHFS_TRACE
+       /*
+        * Disable progress bar if full debug tracing is enabled.
+        * The progress bar in this case just gets in the way of the
+        * debug trace output
+        */
+       progress = FALSE;
+#endif
+
+       if(i == argc) {
+               if(!version) {
+options:
+                       ERROR("SYNTAX: %s [options] filesystem [directories or "
+                               "files to extract]\n", argv[0]);
+                       ERROR("\t-v[ersion]\t\tprint version, licence and "
+                               "copyright information\n");
+                       ERROR("\t-d[est] <pathname>\tunsquash to <pathname>, "
+                               "default \"squashfs-root\"\n");
+                       ERROR("\t-q[uiet]\t\tno verbose output\n");
+                       ERROR("\t-n[o-progress]\t\tdon't display the progress "
+                               "bar\n");
+                       ERROR("\t-no[-xattrs]\t\tdon't extract xattrs in file system"
+                               NOXOPT_STR"\n");
+                       ERROR("\t-x[attrs]\t\textract xattrs in file system"
+                               XOPT_STR "\n");
+                       ERROR("\t-u[ser-xattrs]\t\tonly extract user xattrs in "
+                               "file system.\n\t\t\t\tEnables extracting "
+                               "xattrs\n");
+                       ERROR("\t-p[rocessors] <number>\tuse <number> "
+                               "processors.  By default will use\n");
+                       ERROR("\t\t\t\tnumber of processors available\n");
+                       ERROR("\t-i[nfo]\t\t\tprint files as they are "
+                               "unsquashed\n");
+                       ERROR("\t-li[nfo]\t\tprint files as they are "
+                               "unsquashed with file\n");
+                       ERROR("\t\t\t\tattributes (like ls -l output)\n");
+                       ERROR("\t-l[s]\t\t\tlist filesystem, but don't unsquash"
+                               "\n");
+                       ERROR("\t-ll[s]\t\t\tlist filesystem with file "
+                               "attributes (like\n");
+                       ERROR("\t\t\t\tls -l output), but don't unsquash\n");
+                       ERROR("\t-lln[umeric]\t\t-lls but with numeric uids and gids\n");
+                       ERROR("\t-lc\t\t\tlist filesystem concisely, displaying only"
+                               " files\n\t\t\t\tand empty directories.  Don't unsquash\n");
+                       ERROR("\t-llc\t\t\tlist filesystem concisely with file attributes,"
+                               "\n\t\t\t\tdisplaying only files and empty directories.\n\t\t\t\tDon't unsquash\n");
+                       ERROR("\t-o[ffset] <bytes>\tskip <bytes> at start of <dest>\n");
+                       ERROR("\t\t\t\tOptionally a suffix of K, M or G can be"
+                               " given to specify\n\t\t\t\tKbytes, Mbytes or"
+                               " Gbytes respectively.\n");
+                       ERROR("\t\t\t\tDefault 0 bytes.\n");
+                       ERROR("\t-f[orce]\t\tif file already exists then "
+                               "overwrite\n");
+                       ERROR("\t-ig[nore-errors]\tTreat errors writing files "
+                               "to output as non-fatal\n");
+                       ERROR("\t-st[rict-errors]\tTreat all errors as fatal\n");
+                       ERROR("\t-s[tat]\t\t\tdisplay filesystem superblock "
+                               "information\n");
+                       ERROR("\t-UTC\t\t\tUse UTC rather than local time zone when displaying time\n");
+                       ERROR("\t-mkfs-time\t\tdisplay filesystem superblock time\n");
+                       ERROR("\t-fstime\t\t\tsynonym for -mkfs-time\n");
+                       ERROR("\t-e[f] <extract file>\tlist of directories or "
+                               "files to extract.\n\t\t\t\tOne per line\n");
+                       ERROR("\t-da[ta-queue] <size>\tSet data queue to "
+                               "<size> Mbytes.  Default %d\n\t\t\t\tMbytes\n",
+                               DATA_BUFFER_DEFAULT);
+                       ERROR("\t-fr[ag-queue] <size>\tSet fragment queue to "
+                               "<size> Mbytes.  Default\n\t\t\t\t%d Mbytes\n",
+                               FRAGMENT_BUFFER_DEFAULT);
+                       ERROR("\t-r[egex]\t\ttreat extract names as POSIX "
+                               "regular expressions\n");
+                       ERROR("\t\t\t\trather than use the default shell "
+                               "wildcard\n\t\t\t\texpansion (globbing)\n");
+                       ERROR("\nDecompressors available:\n");
+                       display_compressors("", "");
+               }
+               exit(1);
+       }
+
+       for(n = i + 1; n < argc; n++)
+               path = add_path(path, argv[n], argv[n]);
+
+       if((fd = open(argv[i], O_RDONLY)) == -1) {
+               ERROR("Could not open %s, because %s\n", argv[i],
+                       strerror(errno));
+               exit(1);
+       }
+
+    ventoy_parse_disk_map();
+
+       if(read_super(argv[i]) == FALSE)
+               exit(1);
+
+       if(mkfs_time_opt) {
+               printf("%u\n", sBlk.s.mkfs_time);
+               exit(0);
+       }
+
+       if(stat_sys) {
+               squashfs_stat(argv[i]);
+               exit(0);
+       }
+
+       if(!check_compression(comp))
+               exit(1);
+
+       block_size = sBlk.s.block_size;
+       block_log = sBlk.s.block_log;
+
+       /*
+        * Sanity check block size and block log.
+        *
+        * Check they're within correct limits
+        */
+       if(block_size > SQUASHFS_FILE_MAX_SIZE ||
+                                       block_log > SQUASHFS_FILE_MAX_LOG)
+               EXIT_UNSQUASH("Block size or block_log too large."
+                       "  File system is corrupt.\n");
+
+       /*
+        * Check block_size and block_log match
+        */
+       if(block_size != (1 << block_log))
+               EXIT_UNSQUASH("Block size and block_log do not match."
+                       "  File system is corrupt.\n");
+
+       /*
+        * convert from queue size in Mbytes to queue size in
+        * blocks.
+        *
+        * In doing so, check that the user supplied values do not
+        * overflow a signed int
+        */
+       if(shift_overflow(fragment_buffer_size, 20 - block_log))
+               EXIT_UNSQUASH("Fragment queue size is too large\n");
+       else
+               fragment_buffer_size <<= 20 - block_log;
+
+       if(shift_overflow(data_buffer_size, 20 - block_log))
+               EXIT_UNSQUASH("Data queue size is too large\n");
+       else
+               data_buffer_size <<= 20 - block_log;
+
+       initialise_threads(fragment_buffer_size, data_buffer_size);
+
+       created_inode = malloc(sBlk.s.inodes * sizeof(char *));
+       if(created_inode == NULL)
+               EXIT_UNSQUASH("failed to allocate created_inode\n");
+
+       memset(created_inode, 0, sBlk.s.inodes * sizeof(char *));
+
+       s_ops = read_filesystem_tables();
+       if(s_ops == NULL)
+               EXIT_UNSQUASH("failed to read file system tables\n");
+
+       if(path) {
+               paths = init_subdir();
+               paths = add_subdir(paths, path);
+       }
+
+       if(!quiet || progress) {
+               res = pre_scan(dest, SQUASHFS_INODE_BLK(sBlk.s.root_inode),
+                       SQUASHFS_INODE_OFFSET(sBlk.s.root_inode), paths);
+               if(res == FALSE)
+                       exit_code = 1;
+
+               memset(created_inode, 0, sBlk.s.inodes * sizeof(char *));
+               inode_number = 1;
+
+               if(!quiet)  {
+                       printf("Parallel unsquashfs: Using %d processor%s\n", processors,
+                                       processors == 1 ? "" : "s");
+
+                       printf("%d inodes (%d blocks) to write\n\n", total_inodes,
+                                       total_inodes - total_files + total_blocks);
+               }
+
+               enable_progress_bar();
+       }
+
+       res = dir_scan(dest, SQUASHFS_INODE_BLK(sBlk.s.root_inode),
+               SQUASHFS_INODE_OFFSET(sBlk.s.root_inode), paths);
+       if(res == FALSE)
+               exit_code = 1;
+
+       queue_put(to_writer, NULL);
+       res = (long) queue_get(from_writer);
+       if(res == TRUE)
+               exit_code = 1;
+
+       disable_progress_bar();
+
+       if(!quiet && !lsonly) {
+               printf("\n");
+               printf("created %d files\n", file_count);
+               printf("created %d directories\n", dir_count);
+               printf("created %d symlinks\n", sym_count);
+               printf("created %d devices\n", dev_count);
+               printf("created %d fifos\n", fifo_count);
+       }
+
+    close(fd);    
+
+       return exit_code;
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquashfs.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquashfs.h
new file mode 100644 (file)
index 0000000..934618b
--- /dev/null
@@ -0,0 +1,264 @@
+#ifndef UNSQUASHFS_H
+#define UNSQUASHFS_H
+/*
+ * Unsquash a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2009, 2010, 2013, 2014, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * unsquashfs.h
+ */
+
+#define TRUE 1
+#define FALSE 0
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <utime.h>
+#include <pwd.h>
+#include <grp.h>
+#include <time.h>
+#include <regex.h>
+#include <signal.h>
+#include <pthread.h>
+#include <math.h>
+#include <sys/ioctl.h>
+#include <sys/time.h>
+
+#ifndef linux
+#define __BYTE_ORDER BYTE_ORDER
+#define __BIG_ENDIAN BIG_ENDIAN
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#else
+#include <endian.h>
+#endif
+
+#include "squashfs_fs.h"
+#include "error.h"
+
+#define CALCULATE_HASH(start)  (start & 0xffff)
+
+/*
+ * Unified superblock containing fields for all superblocks
+ */
+struct super_block {
+       struct squashfs_super_block s;
+       /* fields only used by squashfs 3 and earlier layouts */
+       unsigned int            no_uids;
+       unsigned int            no_guids;
+       long long               uid_start;
+       long long               guid_start;
+};
+
+struct hash_table_entry {
+       long long       start;
+       long long       bytes;
+       struct hash_table_entry *next;
+};
+
+struct inode {
+       int blocks;
+       char *block_ptr;
+       long long data;
+       int fragment;
+       int frag_bytes;
+       gid_t gid;
+       int inode_number;
+       int mode;
+       int offset;
+       long long start;
+       char *symlink;
+       time_t time;
+       int type;
+       uid_t uid;
+       char sparse;
+       unsigned int xattr;
+};
+
+typedef struct squashfs_operations {
+       struct dir *(*opendir)(unsigned int block_start,
+               unsigned int offset, struct inode **i);
+       void (*read_fragment)(unsigned int fragment, long long *start_block,
+               int *size);
+       void (*read_block_list)(unsigned int *block_list, char *block_ptr,
+               int blocks);
+       struct inode *(*read_inode)(unsigned int start_block,
+               unsigned int offset);
+} squashfs_operations;
+
+struct test {
+       int mask;
+       int value;
+       int position;
+       char mode;
+};
+
+
+/* Cache status struct.  Caches are used to keep
+  track of memory buffers passed between different threads */
+struct cache {
+       int     max_buffers;
+       int     count;
+       int     used;
+       int     buffer_size;
+       int     wait_free;
+       int     wait_pending;
+       pthread_mutex_t mutex;
+       pthread_cond_t wait_for_free;
+       pthread_cond_t wait_for_pending;
+       struct cache_entry *free_list;
+       struct cache_entry *hash_table[65536];
+};
+
+/* struct describing a cache entry passed between threads */
+struct cache_entry {
+       struct cache *cache;
+       long long block;
+       int     size;
+       int     used;
+       int error;
+       int     pending;
+       struct cache_entry *hash_next;
+       struct cache_entry *hash_prev;
+       struct cache_entry *free_next;
+       struct cache_entry *free_prev;
+       char *data;
+};
+
+/* struct describing queues used to pass data between threads */
+struct queue {
+       int     size;
+       int     readp;
+       int     writep;
+       pthread_mutex_t mutex;
+       pthread_cond_t empty;
+       pthread_cond_t full;
+       void **data;
+};
+
+/* default size of fragment buffer in Mbytes */
+#define FRAGMENT_BUFFER_DEFAULT 256
+/* default size of data buffer in Mbytes */
+#define DATA_BUFFER_DEFAULT 256
+
+#define DIR_ENT_SIZE   16
+
+struct dir_ent {
+       char            name[SQUASHFS_NAME_LEN + 1];
+       unsigned int    start_block;
+       unsigned int    offset;
+       unsigned int    type;
+};
+
+struct dir {
+       int             dir_count;
+       int             cur_entry;
+       unsigned int    mode;
+       uid_t           uid;
+       gid_t           guid;
+       unsigned int    mtime;
+       unsigned int xattr;
+       struct dir_ent  *dirs;
+};
+
+struct file_entry {
+       int offset;
+       int size;
+       struct cache_entry *buffer;
+};
+
+
+struct squashfs_file {
+       int fd;
+       int blocks;
+       long long file_size;
+       int mode;
+       uid_t uid;
+       gid_t gid;
+       time_t time;
+       char *pathname;
+       char sparse;
+       unsigned int xattr;
+};
+
+struct path_entry {
+       char *name;
+       regex_t *preg;
+       struct pathname *paths;
+};
+
+struct pathname {
+       int names;
+       struct path_entry *name;
+};
+
+struct pathnames {
+       int count;
+       struct pathname *path[0];
+};
+#define PATHS_ALLOC_SIZE 10
+
+/* globals */
+extern struct super_block sBlk;
+extern int swap;
+extern struct hash_table_entry *inode_table_hash[65536],
+       *directory_table_hash[65536];
+extern pthread_mutex_t screen_mutex;
+extern int progress_enabled;
+extern int inode_number;
+extern int lookup_type[];
+extern int fd;
+extern int no_xattrs;
+extern struct queue *to_reader, *to_inflate, *to_writer;
+extern struct cache *fragment_cache, *data_cache;
+
+/* unsquashfs.c */
+extern void *read_inode_table(long long, long long);
+extern void *read_directory_table(long long, long long);
+extern long long lookup_entry(struct hash_table_entry **, long long);
+extern int read_fs_bytes(int fd, long long, int, void *);
+extern int read_block(int, long long, long long *, int, void *);
+extern void enable_progress_bar();
+extern void disable_progress_bar();
+extern void dump_queue(struct queue *);
+extern void dump_cache(struct cache *);
+
+/* unsquash-1.c */
+extern squashfs_operations *read_filesystem_tables_1();
+
+/* unsquash-2.c */
+extern squashfs_operations *read_filesystem_tables_2();
+
+/* unsquash-3.c */
+extern squashfs_operations *read_filesystem_tables_3();
+
+/* unsquash-4.c */
+extern squashfs_operations *read_filesystem_tables_4();
+
+/* unsquash-123.c */
+extern int read_ids(int, long long, long long, unsigned int **);
+
+/* unsquash-34.c */
+extern long long *alloc_index_table(int);
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquashfs_info.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquashfs_info.c
new file mode 100644 (file)
index 0000000..c8e2b9b
--- /dev/null
@@ -0,0 +1,145 @@
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * unsquashfs_info.c
+ */
+
+#include <pthread.h>
+#include <sys/ioctl.h>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/time.h>
+#include <stdio.h>
+#include <math.h>
+#include <stdarg.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <dirent.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <string.h>
+
+#include "squashfs_fs.h"
+#include "unsquashfs.h"
+#include "error.h"
+
+static int silent = 0;
+char *pathname = NULL;
+
+pthread_t info_thread;
+
+
+void disable_info()
+{
+       if(pathname)
+               free(pathname);
+
+       pathname = NULL;
+}
+
+
+void update_info(char *name)
+{
+       if(pathname)
+               free(pathname);
+
+       pathname = name;
+}
+
+
+void dump_state()
+{
+       disable_progress_bar();
+
+       printf("Queue and cache status dump\n");
+       printf("===========================\n");
+
+       printf("file buffer read queue (main thread -> reader thread)\n");
+       dump_queue(to_reader);
+
+       printf("file buffer decompress queue (reader thread -> inflate"
+                                                       " thread(s))\n");
+       dump_queue(to_inflate);
+
+       printf("file buffer write queue (main thread -> writer thread)\n");
+       dump_queue(to_writer);
+
+       printf("\nbuffer cache (uncompressed blocks and compressed blocks "
+                                                       "'in flight')\n");
+       dump_cache(data_cache);
+
+       printf("fragment buffer cache (uncompressed frags and compressed"
+                                               " frags 'in flight')\n");
+       dump_cache(fragment_cache);
+
+       enable_progress_bar();
+}
+
+
+void *info_thrd(void *arg)
+{
+       sigset_t sigmask;
+       struct timespec timespec = { .tv_sec = 1, .tv_nsec = 0 };
+       int sig, waiting = 0;
+
+       sigemptyset(&sigmask);
+       sigaddset(&sigmask, SIGQUIT);
+       sigaddset(&sigmask, SIGHUP);
+
+       while(1) {
+               if(waiting)
+                       sig = sigtimedwait(&sigmask, NULL, &timespec);
+               else
+                       sig = sigwaitinfo(&sigmask, NULL);
+
+               if(sig == -1) {
+                       switch(errno) {
+                       case EAGAIN:
+                               /* interval timed out */
+                               waiting = 0;
+                               /* FALLTHROUGH */
+                       case EINTR:
+                               /* if waiting, the wait will be longer, but
+                                  that's OK */
+                               continue;
+                       default:
+                               BAD_ERROR("sigtimedwait/sigwaitinfo failed "
+                                       "because %s\n", strerror(errno));
+                       }
+               }
+
+               if(sig == SIGQUIT && !waiting) {
+                       if(pathname)
+                               INFO("%s\n", pathname);
+
+                       /* set one second interval period, if ^\ received
+                          within then, dump queue and cache status */
+                       waiting = 1;
+               } else
+                       dump_state();
+       }
+}
+
+
+void init_info()
+{
+       pthread_create(&info_thread, NULL, info_thrd, NULL);
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquashfs_info.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquashfs_info.h
new file mode 100644 (file)
index 0000000..f85efd1
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef UNSQUASHFS_INFO_H
+#define UNSQUASHFS_INFO_H
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2013, 2014
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * unsquashfs_info.h
+ */
+
+extern void disable_info();
+extern void update_info(char *);
+extern void init_info();
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquashfs_xattr.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/unsquashfs_xattr.c
new file mode 100644 (file)
index 0000000..7742dfe
--- /dev/null
@@ -0,0 +1,144 @@
+/*
+ * Unsquash a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2010, 2012, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * unsquashfs_xattr.c
+ */
+
+#include "unsquashfs.h"
+#include "xattr.h"
+
+#include <sys/xattr.h>
+
+#define NOSPACE_MAX 10
+
+extern int root_process;
+extern int user_xattrs;
+extern int ignore_errors;
+extern int strict_errors;
+
+int write_xattr(char *pathname, unsigned int xattr)
+{
+       unsigned int count;
+       struct xattr_list *xattr_list;
+       int i;
+       static int nonsuper_error = FALSE;
+       static int ignore_xattrs = FALSE;
+       static int nospace_error = 0;
+       int failed;
+
+       if(ignore_xattrs || xattr == SQUASHFS_INVALID_XATTR ||
+                       sBlk.s.xattr_id_table_start == SQUASHFS_INVALID_BLK)
+               return TRUE;
+
+       xattr_list = get_xattr(xattr, &count, &failed);
+       if(failed)
+               EXIT_UNSQUASH_STRICT("write_xattr: Failed to read one or more xattrs for %s\n", pathname);
+
+       for(i = 0; i < count; i++) {
+               int prefix = xattr_list[i].type & SQUASHFS_XATTR_PREFIX_MASK;
+
+               if(ignore_xattrs || (user_xattrs && prefix != SQUASHFS_XATTR_USER))
+                       continue;
+
+               if(root_process || prefix == SQUASHFS_XATTR_USER) {
+                       int res = lsetxattr(pathname, xattr_list[i].full_name,
+                               xattr_list[i].value, xattr_list[i].vsize, 0);
+
+                       if(res == -1) {
+                               if(errno == ENOTSUP) {
+                                       /*
+                                        * If the destination filesystem cannot
+                                        * suppport xattrs, print error, and
+                                        * disable xattr output as this error is
+                                        * unlikely to go away, and printing
+                                        * screenfulls of the same error message
+                                        * is rather annoying
+                                        */
+                                       ERROR("write_xattr: failed to write "
+                                               "xattr %s for file %s because " 
+                                               "extended attributes are not "
+                                               "supported by the destination "
+                                               "filesystem\n",
+                                               xattr_list[i].full_name,
+                                               pathname);
+                                       ERROR("Ignoring xattrs in "
+                                                               "filesystem\n");
+                                       EXIT_UNSQUASH_STRICT("To avoid this error message, "
+                                               "specify -no-xattrs\n");
+                                       ignore_xattrs = TRUE;
+                               } else if((errno == ENOSPC || errno == EDQUOT)
+                                               && nospace_error < NOSPACE_MAX) {
+                                       /*
+                                        * Many filesystems like ext2/3/4 have
+                                        * limits on the amount of xattr
+                                        * data that can be stored per file
+                                        * (typically one block or 4K), so
+                                        * we shouldn't disable xattr ouput,
+                                        * as the error may be restriced to one
+                                        * file only.  If we get a lot of these
+                                        * then suppress the error messsage
+                                        */
+                                       EXIT_UNSQUASH_IGNORE("write_xattr: failed to write "
+                                               "xattr %s for file %s because " 
+                                               "no extended attribute space "
+                                               "remaining (per file or "
+                                               "filesystem limit)\n",
+                                               xattr_list[i].full_name,
+                                               pathname);
+                                       if(++ nospace_error == NOSPACE_MAX)
+                                               ERROR("%d of these errors "
+                                                       "printed, further error "
+                                                       "messages of this type "
+                                                       "are suppressed!\n",
+                                                       NOSPACE_MAX);
+                               } else
+                                       EXIT_UNSQUASH_IGNORE("write_xattr: failed to write "
+                                               "xattr %s for file %s because "
+                                               "%s\n", xattr_list[i].full_name,
+                                               pathname, strerror(errno));
+                               failed = TRUE;
+                       }
+               } else if(nonsuper_error == FALSE) {
+                       /*
+                        * if extract user xattrs only then
+                        * error message is suppressed, if not
+                        * print error, and then suppress further error
+                        * messages to avoid possible screenfulls of the
+                        * same error message!
+                        */
+                       ERROR("write_xattr: could not write xattr %s "
+                                       "for file %s because you're not "
+                                       "superuser!\n",
+                                       xattr_list[i].full_name, pathname);
+                       EXIT_UNSQUASH_STRICT("write_xattr: to avoid this error message, either"
+                               " specify -user-xattrs, -no-xattrs, or run as "
+                               "superuser!\n");
+                       ERROR("Further error messages of this type are "
+                               "suppressed!\n");
+                       nonsuper_error = TRUE;
+                       failed = TRUE;
+               }
+       }
+
+       free_xattr(xattr_list, count);
+
+       return !failed;
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/xattr.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/xattr.c
new file mode 100644 (file)
index 0000000..64dfd82
--- /dev/null
@@ -0,0 +1,719 @@
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2008, 2009, 2010, 2012, 2014, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * xattr.c
+ */
+
+#define TRUE 1
+#define FALSE 0
+
+#include <unistd.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <errno.h>
+#include <dirent.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/xattr.h>
+
+#include "squashfs_fs.h"
+#include "squashfs_swap.h"
+#include "mksquashfs.h"
+#include "xattr.h"
+#include "error.h"
+#include "progressbar.h"
+
+/* compressed xattr table */
+static char *xattr_table = NULL;
+static unsigned int xattr_size = 0;
+
+/* cached uncompressed xattr data */
+static char *data_cache = NULL;
+static int cache_bytes = 0, cache_size = 0;
+
+/* cached uncompressed xattr id table */
+static struct squashfs_xattr_id *xattr_id_table = NULL;
+static int xattr_ids = 0;
+
+/* saved compressed xattr table */
+unsigned int sxattr_bytes = 0, stotal_xattr_bytes = 0;
+
+/* saved cached uncompressed xattr data */
+static char *sdata_cache = NULL;
+static int scache_bytes = 0;
+
+/* saved cached uncompressed xattr id table */
+static int sxattr_ids = 0;
+
+/* xattr hash table for value duplicate detection */
+static struct xattr_list *dupl_value[65536];
+
+/* xattr hash table for id duplicate detection */
+static struct dupl_id *dupl_id[65536];
+
+/* file system globals from mksquashfs.c */
+extern int no_xattrs, noX;
+extern long long bytes;
+extern int fd;
+extern unsigned int xattr_bytes, total_xattr_bytes;
+
+/* helper functions from mksquashfs.c */
+extern unsigned short get_checksum(char *, int, unsigned short);
+extern void write_destination(int, long long, int, void *);
+extern long long generic_write_table(int, void *, int, void *, int);
+extern int mangle(char *, char *, int, int, int, int);
+extern char *pathname(struct dir_ent *);
+
+/* helper functions and definitions from read_xattrs.c */
+extern int read_xattrs_from_disk(int, struct squashfs_super_block *, int, long long *);
+extern struct xattr_list *get_xattr(int, unsigned int *, int *);
+extern struct prefix prefix_table[];
+
+
+static int get_prefix(struct xattr_list *xattr, char *name)
+{
+       int i;
+
+       xattr->full_name = strdup(name);
+
+       for(i = 0; prefix_table[i].type != -1; i++) {
+               struct prefix *p = &prefix_table[i];
+               if(strncmp(xattr->full_name, p->prefix, strlen(p->prefix)) == 0)
+                       break;
+       }
+
+       if(prefix_table[i].type != -1) {
+               xattr->name = xattr->full_name + strlen(prefix_table[i].prefix);
+               xattr->size = strlen(xattr->name);
+       }
+
+       return prefix_table[i].type;
+}
+
+       
+static int read_xattrs_from_system(char *filename, struct xattr_list **xattrs)
+{
+       ssize_t size, vsize;
+       char *xattr_names, *p;
+       int i;
+       struct xattr_list *xattr_list = NULL;
+
+       while(1) {
+               size = llistxattr(filename, NULL, 0);
+               if(size <= 0) {
+                       if(size < 0 && errno != ENOTSUP) {
+                               ERROR_START("llistxattr for %s failed in "
+                                       "read_attrs, because %s", filename,
+                                       strerror(errno));
+                               ERROR_EXIT(".  Ignoring");
+                       }
+                       return 0;
+               }
+
+               xattr_names = malloc(size);
+               if(xattr_names == NULL)
+                       MEM_ERROR();
+
+               size = llistxattr(filename, xattr_names, size);
+               if(size < 0) {
+                       free(xattr_names);
+                       if(errno == ERANGE)
+                               /* xattr list grew?  Try again */
+                               continue;
+                       else {
+                               ERROR_START("llistxattr for %s failed in "
+                                       "read_attrs, because %s", filename,
+                                       strerror(errno));
+                               ERROR_EXIT(".  Ignoring");
+                               return 0;
+                       }
+               }
+
+               break;
+       }
+
+       for(i = 0, p = xattr_names; p < xattr_names + size; i++) {
+               struct xattr_list *x = realloc(xattr_list, (i + 1) *
+                                               sizeof(struct xattr_list));
+               if(x == NULL)
+                       MEM_ERROR();
+               xattr_list = x;
+
+               xattr_list[i].type = get_prefix(&xattr_list[i], p);
+               p += strlen(p) + 1;
+               if(xattr_list[i].type == -1) {
+                       ERROR("Unrecognised xattr prefix %s\n",
+                               xattr_list[i].full_name);
+                       free(xattr_list[i].full_name);
+                       i--;
+                       continue;
+               }
+
+               while(1) {
+                       vsize = lgetxattr(filename, xattr_list[i].full_name,
+                                                               NULL, 0);
+                       if(vsize < 0) {
+                               ERROR_START("lgetxattr failed for %s in "
+                                       "read_attrs, because %s", filename,
+                                       strerror(errno));
+                               ERROR_EXIT(".  Ignoring");
+                               free(xattr_list[i].full_name);
+                               goto failed;
+                       }
+
+                       xattr_list[i].value = malloc(vsize);
+                       if(xattr_list[i].value == NULL)
+                               MEM_ERROR();
+
+                       vsize = lgetxattr(filename, xattr_list[i].full_name,
+                                               xattr_list[i].value, vsize);
+                       if(vsize < 0) {
+                               free(xattr_list[i].value);
+                               if(errno == ERANGE)
+                                       /* xattr grew?  Try again */
+                                       continue;
+                               else {
+                                       ERROR_START("lgetxattr failed for %s "
+                                               "in read_attrs, because %s",
+                                               filename, strerror(errno));
+                                       ERROR_EXIT(".  Ignoring");
+                                       free(xattr_list[i].full_name);
+                                       goto failed;
+                               }
+                       }
+                       
+                       break;
+               }
+               xattr_list[i].vsize = vsize;
+
+               TRACE("read_xattrs_from_system: filename %s, xattr name %s,"
+                       " vsize %d\n", filename, xattr_list[i].full_name,
+                       xattr_list[i].vsize);
+       }
+       free(xattr_names);
+       if(i > 0)
+               *xattrs = xattr_list;
+       else
+               free(xattr_list);
+       return i;
+
+failed:
+       while(--i >= 0) {
+               free(xattr_list[i].full_name);
+               free(xattr_list[i].value);
+       }
+       free(xattr_list);
+       free(xattr_names);
+       return 0;
+}
+
+
+static int get_xattr_size(struct xattr_list *xattr)
+{
+       int size = sizeof(struct squashfs_xattr_entry) +
+               sizeof(struct squashfs_xattr_val) + xattr->size;
+
+       if(xattr->type & XATTR_VALUE_OOL)
+               size += XATTR_VALUE_OOL_SIZE;
+       else
+               size += xattr->vsize;
+
+       return size;
+}
+
+
+static void *get_xattr_space(unsigned int req_size, long long *disk)
+{
+       int data_space;
+       unsigned short c_byte;
+
+       /*
+        * Move and compress cached uncompressed data into xattr table.
+        */
+       while(cache_bytes >= SQUASHFS_METADATA_SIZE) {
+               if((xattr_size - xattr_bytes) <
+                               ((SQUASHFS_METADATA_SIZE << 1)) + 2) {
+                       xattr_table = realloc(xattr_table, xattr_size +
+                               (SQUASHFS_METADATA_SIZE << 1) + 2);
+                       if(xattr_table == NULL)
+                               MEM_ERROR();
+                       xattr_size += (SQUASHFS_METADATA_SIZE << 1) + 2;
+               }
+
+               c_byte = mangle(xattr_table + xattr_bytes + BLOCK_OFFSET,
+                       data_cache, SQUASHFS_METADATA_SIZE,
+                       SQUASHFS_METADATA_SIZE, noX, 0);
+               TRACE("Xattr block @ 0x%x, size %d\n", xattr_bytes, c_byte);
+               SQUASHFS_SWAP_SHORTS(&c_byte, xattr_table + xattr_bytes, 1);
+               xattr_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) + BLOCK_OFFSET;
+               memmove(data_cache, data_cache + SQUASHFS_METADATA_SIZE,
+                       cache_bytes - SQUASHFS_METADATA_SIZE);
+               cache_bytes -= SQUASHFS_METADATA_SIZE;
+       }
+
+       /*
+        * Ensure there's enough space in the uncompressed data cache
+        */
+       data_space = cache_size - cache_bytes;
+       if(data_space < req_size) {
+                       int realloc_size = req_size - data_space;
+                       data_cache = realloc(data_cache, cache_size +
+                               realloc_size);
+                       if(data_cache == NULL)
+                               MEM_ERROR();
+                       cache_size += realloc_size;
+       }
+
+       if(disk)
+               *disk = ((long long) xattr_bytes << 16) | cache_bytes;
+       cache_bytes += req_size;
+       return data_cache + cache_bytes - req_size;
+}
+
+
+static struct dupl_id *check_id_dupl(struct xattr_list *xattr_list, int xattrs)
+{
+       struct dupl_id *entry;
+       int i;
+       unsigned short checksum = 0;
+
+       /* compute checksum over all xattrs */
+       for(i = 0; i < xattrs; i++) {
+               struct xattr_list *xattr = &xattr_list[i];
+
+               checksum = get_checksum(xattr->full_name,
+                                       strlen(xattr->full_name), checksum);
+               checksum = get_checksum(xattr->value,
+                                       xattr->vsize, checksum);
+       }
+
+       for(entry = dupl_id[checksum]; entry; entry = entry->next) {
+               if (entry->xattrs != xattrs)
+                       continue;
+
+               for(i = 0; i < xattrs; i++) {
+                       struct xattr_list *xattr = &xattr_list[i];
+                       struct xattr_list *dup_xattr = &entry->xattr_list[i];
+
+                       if(strcmp(xattr->full_name, dup_xattr->full_name))
+                               break;
+
+                       if(xattr->vsize != dup_xattr->vsize)
+                               break;
+
+                       if(memcmp(xattr->value, dup_xattr->value, xattr->vsize))
+                               break;
+               }
+               
+               if(i == xattrs)
+                       break;
+       }
+
+       if(entry == NULL) {
+               /* no duplicate exists */
+               entry = malloc(sizeof(*entry));
+               if(entry == NULL)
+                       MEM_ERROR();
+               entry->xattrs = xattrs;
+               entry->xattr_list = xattr_list;
+               entry->xattr_id = SQUASHFS_INVALID_XATTR;
+               entry->next = dupl_id[checksum];
+               dupl_id[checksum] = entry;
+       }
+               
+       return entry;
+}
+
+
+static void check_value_dupl(struct xattr_list *xattr)
+{
+       struct xattr_list *entry;
+
+       if(xattr->vsize < XATTR_VALUE_OOL_SIZE)
+               return;
+
+       /* Check if this is a duplicate of an existing value */
+       xattr->vchecksum = get_checksum(xattr->value, xattr->vsize, 0);
+       for(entry = dupl_value[xattr->vchecksum]; entry; entry = entry->vnext) {
+               if(entry->vsize != xattr->vsize)
+                       continue;
+               
+               if(memcmp(entry->value, xattr->value, xattr->vsize) == 0)
+                       break;
+       }
+
+       if(entry == NULL) {
+               /*
+                * No duplicate exists, add to hash table, and mark as
+                * requiring writing
+                */
+               xattr->vnext = dupl_value[xattr->vchecksum];
+               dupl_value[xattr->vchecksum] = xattr;
+               xattr->ool_value = SQUASHFS_INVALID_BLK;
+       } else {
+               /*
+                * Duplicate exists, make type XATTR_VALUE_OOL, and
+                * remember where the duplicate is
+                */
+               xattr->type |= XATTR_VALUE_OOL;
+               xattr->ool_value = entry->ool_value;
+               /* on appending don't free duplicate values because the
+                * duplicate value already points to the non-duplicate value */
+               if(xattr->value != entry->value) {
+                       free(xattr->value);
+                       xattr->value = entry->value;
+               }
+       }
+}
+
+
+static int get_xattr_id(int xattrs, struct xattr_list *xattr_list,
+               long long xattr_disk, struct dupl_id *xattr_dupl)
+{
+       int i, size = 0;
+       struct squashfs_xattr_id *xattr_id;
+
+       xattr_id_table = realloc(xattr_id_table, (xattr_ids + 1) *
+               sizeof(struct squashfs_xattr_id));
+       if(xattr_id_table == NULL)
+               MEM_ERROR();
+
+       /* get total uncompressed size of xattr data, needed for stat */
+       for(i = 0; i < xattrs; i++)
+               size += strlen(xattr_list[i].full_name) + 1 +
+                       xattr_list[i].vsize;
+
+       xattr_id = &xattr_id_table[xattr_ids];
+       xattr_id->xattr = xattr_disk;
+       xattr_id->count = xattrs;
+       xattr_id->size = size;
+
+       /*
+        * keep track of total uncompressed xattr data, needed for mksquashfs
+        * file system summary
+        */
+       total_xattr_bytes += size;
+
+       xattr_dupl->xattr_id = xattr_ids ++;
+       return xattr_dupl->xattr_id;
+}
+       
+
+long long write_xattrs()
+{
+       unsigned short c_byte;
+       int i, avail_bytes;
+       char *datap = data_cache;
+       long long start_bytes = bytes;
+       struct squashfs_xattr_table header;
+
+       if(xattr_ids == 0)
+               return SQUASHFS_INVALID_BLK;
+
+       /*
+        * Move and compress cached uncompressed data into xattr table.
+        */
+       while(cache_bytes) {
+               if((xattr_size - xattr_bytes) <
+                               ((SQUASHFS_METADATA_SIZE << 1)) + 2) {
+                       xattr_table = realloc(xattr_table, xattr_size +
+                               (SQUASHFS_METADATA_SIZE << 1) + 2);
+                       if(xattr_table == NULL)
+                               MEM_ERROR();
+                       xattr_size += (SQUASHFS_METADATA_SIZE << 1) + 2;
+               }
+
+               avail_bytes = cache_bytes > SQUASHFS_METADATA_SIZE ?
+                       SQUASHFS_METADATA_SIZE : cache_bytes;
+               c_byte = mangle(xattr_table + xattr_bytes + BLOCK_OFFSET, datap,
+                       avail_bytes, SQUASHFS_METADATA_SIZE, noX, 0);
+               TRACE("Xattr block @ 0x%x, size %d\n", xattr_bytes, c_byte);
+               SQUASHFS_SWAP_SHORTS(&c_byte, xattr_table + xattr_bytes, 1);
+               xattr_bytes += SQUASHFS_COMPRESSED_SIZE(c_byte) + BLOCK_OFFSET;
+               datap += avail_bytes;
+               cache_bytes -= avail_bytes;
+       }
+
+       /*
+        * Write compressed xattr table to file system
+        */
+       write_destination(fd, bytes, xattr_bytes, xattr_table);
+        bytes += xattr_bytes;
+
+       /*
+        * Swap if necessary the xattr id table
+        */
+       for(i = 0; i < xattr_ids; i++)
+               SQUASHFS_INSWAP_XATTR_ID(&xattr_id_table[i]);
+
+       header.xattr_ids = xattr_ids;
+       header.xattr_table_start = start_bytes;
+       SQUASHFS_INSWAP_XATTR_TABLE(&header);
+
+       return generic_write_table(xattr_ids * sizeof(struct squashfs_xattr_id),
+               xattr_id_table, sizeof(header), &header, noX);
+}
+
+
+int generate_xattrs(int xattrs, struct xattr_list *xattr_list)
+{
+       int total_size, i;
+       int xattr_value_max;
+       void *xp;
+       long long xattr_disk;
+       struct dupl_id *xattr_dupl;
+
+       /*
+        * check if the file xattrs are a complete duplicate of a pre-existing
+        * id
+        */
+       xattr_dupl = check_id_dupl(xattr_list, xattrs);
+       if(xattr_dupl->xattr_id != SQUASHFS_INVALID_XATTR)
+               return xattr_dupl->xattr_id;
+        
+       /*
+        * Scan the xattr_list deciding which type to assign to each
+        * xattr.  The choice is fairly straightforward, and depends on the
+        * size of each xattr name/value and the overall size of the
+        * resultant xattr list stored in the xattr metadata table.
+        *
+        * Choices are whether to store data inline or out of line.
+        *
+        * The overall goal is to optimise xattr scanning and lookup, and
+        * to enable the file system layout to scale from a couple of
+        * small xattr name/values to a large number of large xattr
+        * names/values without affecting performance.  While hopefully
+        * enabling the common case of a couple of small xattr name/values
+        * to be stored efficiently
+        *
+        * Code repeatedly scans, doing the following
+        *              move xattr data out of line if it exceeds
+        *              xattr_value_max.  Where xattr_value_max is
+        *              initially XATTR_INLINE_MAX.  If the final uncompressed
+        *              xattr list is larger than XATTR_TARGET_MAX then more
+        *              aggressively move xattr data out of line by repeatedly
+        *              setting inline threshold to 1/2, then 1/4, 1/8 of
+        *              XATTR_INLINE_MAX until target achieved or there's
+        *              nothing left to move out of line
+        */
+       xattr_value_max = XATTR_INLINE_MAX;
+       while(1) {
+               for(total_size = 0, i = 0; i < xattrs; i++) {
+                       struct xattr_list *xattr = &xattr_list[i];
+                       xattr->type &= XATTR_PREFIX_MASK; /* all inline */
+                       if (xattr->vsize > xattr_value_max)
+                               xattr->type |= XATTR_VALUE_OOL;
+
+                       total_size += get_xattr_size(xattr);
+               }
+
+               /*
+                * If the total size of the uncompressed xattr list is <=
+                * XATTR_TARGET_MAX we're done
+                */
+               if(total_size <= XATTR_TARGET_MAX)
+                       break;
+
+               if(xattr_value_max == XATTR_VALUE_OOL_SIZE)
+                       break;
+
+               /*
+                * Inline target not yet at minimum and so reduce it, and
+                * try again
+                */
+               xattr_value_max /= 2;
+               if(xattr_value_max < XATTR_VALUE_OOL_SIZE)
+                       xattr_value_max = XATTR_VALUE_OOL_SIZE;
+       }
+
+       /*
+        * Check xattr values for duplicates
+        */
+       for(i = 0; i < xattrs; i++) {
+               check_value_dupl(&xattr_list[i]);
+       }
+
+       /*
+        * Add each out of line value to the file system xattr table
+        * if it doesn't already exist as a duplicate
+        */
+       for(i = 0; i < xattrs; i++) {
+               struct xattr_list *xattr = &xattr_list[i];
+
+               if((xattr->type & XATTR_VALUE_OOL) &&
+                               (xattr->ool_value == SQUASHFS_INVALID_BLK)) {
+                       struct squashfs_xattr_val val;
+                       int size = sizeof(val) + xattr->vsize;
+                       xp = get_xattr_space(size, &xattr->ool_value);
+                       val.vsize = xattr->vsize;
+                       SQUASHFS_SWAP_XATTR_VAL(&val, xp);
+                       memcpy(xp + sizeof(val), xattr->value, xattr->vsize);
+               }
+       }
+
+       /*
+        * Create xattr list and add to file system xattr table
+        */
+       get_xattr_space(0, &xattr_disk);
+       for(i = 0; i < xattrs; i++) {
+               struct xattr_list *xattr = &xattr_list[i];
+               struct squashfs_xattr_entry entry;
+               struct squashfs_xattr_val val;
+
+               xp = get_xattr_space(sizeof(entry) + xattr->size, NULL);
+               entry.type = xattr->type;
+               entry.size = xattr->size;
+               SQUASHFS_SWAP_XATTR_ENTRY(&entry, xp);
+               memcpy(xp + sizeof(entry), xattr->name, xattr->size);
+
+               if(xattr->type & XATTR_VALUE_OOL) {
+                       int size = sizeof(val) + XATTR_VALUE_OOL_SIZE;
+                       xp = get_xattr_space(size, NULL);
+                       val.vsize = XATTR_VALUE_OOL_SIZE;
+                       SQUASHFS_SWAP_XATTR_VAL(&val, xp);
+                       SQUASHFS_SWAP_LONG_LONGS(&xattr->ool_value, xp +
+                               sizeof(val), 1);
+               } else {
+                       int size = sizeof(val) + xattr->vsize;
+                       xp = get_xattr_space(size, &xattr->ool_value);
+                       val.vsize = xattr->vsize;
+                       SQUASHFS_SWAP_XATTR_VAL(&val, xp);
+                       memcpy(xp + sizeof(val), xattr->value, xattr->vsize);
+               }
+       }
+
+       /*
+        * Add to xattr id lookup table
+        */
+       return get_xattr_id(xattrs, xattr_list, xattr_disk, xattr_dupl);
+}
+
+
+int read_xattrs(void *d)
+{
+       struct dir_ent *dir_ent = d;
+       struct inode_info *inode = dir_ent->inode;
+       char *filename = pathname(dir_ent);
+       struct xattr_list *xattr_list;
+       int xattrs;
+
+       if(no_xattrs || IS_PSEUDO(inode) || inode->root_entry)
+               return SQUASHFS_INVALID_XATTR;
+
+       xattrs = read_xattrs_from_system(filename, &xattr_list);
+       if(xattrs == 0)
+               return SQUASHFS_INVALID_XATTR;
+
+       return generate_xattrs(xattrs, xattr_list);
+}
+
+
+/*
+ * Add the existing xattr ids and xattr metadata in the file system being
+ * appended to, to the in-memory xattr cache.  This allows duplicate checking to
+ * take place against the xattrs already in the file system being appended to,
+ * and ensures the pre-existing xattrs are written out along with any new xattrs
+ */
+int get_xattrs(int fd, struct squashfs_super_block *sBlk)
+{
+       int ids, res, i, id;
+       unsigned int count;
+
+       TRACE("get_xattrs\n");
+
+       res = read_xattrs_from_disk(fd, sBlk, FALSE, NULL);
+       if(res == SQUASHFS_INVALID_BLK || res == 0)
+               goto done;
+       ids = res;
+
+       /*
+        * for each xattr id read and construct its list of xattr
+        * name:value pairs, and add them to the in-memory xattr cache
+        */
+       for(i = 0; i < ids; i++) {
+               struct xattr_list *xattr_list = get_xattr(i, &count, &res);
+               if(res) {
+                       free_xattr(xattr_list, count);
+                       return FALSE;
+               }
+               id = generate_xattrs(count, xattr_list);
+
+               /*
+                * Sanity check, the new xattr id should be the same as the
+                * xattr id in the original file system
+                */
+               if(id != i) {
+                       ERROR("BUG, different xattr_id in get_xattrs\n");
+                       res = 0;
+                       goto done;
+               }
+       }
+
+done:
+       return res;
+}
+
+
+/*
+ * Save current state of xattrs, needed for restoring state in the event of an
+ * abort in appending
+ */
+void save_xattrs()
+{
+       /* save the current state of the compressed xattr data */
+       sxattr_bytes = xattr_bytes;
+       stotal_xattr_bytes = total_xattr_bytes;
+
+       /*
+        * save the current state of the cached uncompressed xattr data.
+        * Note we have to save the contents of the data cache because future
+        * operations will delete the current contents
+        */
+       sdata_cache = malloc(cache_bytes);
+       if(sdata_cache == NULL)
+               MEM_ERROR();
+
+       memcpy(sdata_cache, data_cache, cache_bytes);
+       scache_bytes = cache_bytes;
+
+       /* save the current state of the xattr id table */
+       sxattr_ids = xattr_ids;
+}
+
+
+/*
+ * Restore xattrs in the event of an abort in appending
+ */
+void restore_xattrs()
+{
+       /* restore the state of the compressed xattr data */
+       xattr_bytes = sxattr_bytes;
+       total_xattr_bytes = stotal_xattr_bytes;
+
+       /* restore the state of the uncomoressed xattr data */
+       memcpy(data_cache, sdata_cache, scache_bytes);
+       cache_bytes = scache_bytes;
+
+       /* restore the state of the xattr id table */
+       xattr_ids = sxattr_ids;
+}
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/xattr.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/xattr.h
new file mode 100644 (file)
index 0000000..1499bcb
--- /dev/null
@@ -0,0 +1,151 @@
+#ifndef XATTR_H
+#define XATTR_H
+/*
+ * Create a squashfs filesystem.  This is a highly compressed read only
+ * filesystem.
+ *
+ * Copyright (c) 2010, 2012, 2013, 2014, 2019
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * xattr.h
+ */
+
+#define XATTR_VALUE_OOL                SQUASHFS_XATTR_VALUE_OOL
+#define XATTR_PREFIX_MASK      SQUASHFS_XATTR_PREFIX_MASK
+
+#define XATTR_VALUE_OOL_SIZE   sizeof(long long)
+
+/* maximum size of xattr value data that will be inlined */
+#define XATTR_INLINE_MAX       128
+
+/* the target size of an inode's xattr name:value list.  If it
+ * exceeds this, then xattr value data will be successively out of lined
+ * until it meets the target */
+#define XATTR_TARGET_MAX       65536
+
+#define IS_XATTR(a)            (a != SQUASHFS_INVALID_XATTR)
+
+struct xattr_list {
+       char                    *name;
+       char                    *full_name;
+       int                     size;
+       int                     vsize;
+       void                    *value;
+       int                     type;
+       long long               ool_value;
+       unsigned short          vchecksum;
+       struct xattr_list       *vnext;
+};
+
+struct dupl_id {
+       struct xattr_list       *xattr_list;
+       int                     xattrs;
+       int                     xattr_id;
+       struct dupl_id          *next;
+};
+
+struct prefix {
+       char                    *prefix;
+       int                     type;
+};
+
+extern int generate_xattrs(int, struct xattr_list *);
+
+#ifdef XATTR_SUPPORT
+extern int get_xattrs(int, struct squashfs_super_block *);
+extern int read_xattrs(void *);
+extern long long write_xattrs();
+extern void save_xattrs();
+extern void restore_xattrs();
+extern unsigned int xattr_bytes, total_xattr_bytes;
+extern int write_xattr(char *, unsigned int);
+extern int read_xattrs_from_disk(int, struct squashfs_super_block *, int, long long *);
+extern struct xattr_list *get_xattr(int, unsigned int *, int *);
+extern void free_xattr(struct xattr_list *, int);
+#else
+static inline int get_xattrs(int fd, struct squashfs_super_block *sBlk)
+{
+       if(sBlk->xattr_id_table_start != SQUASHFS_INVALID_BLK) {
+               fprintf(stderr, "Xattrs in filesystem! These are not "
+                       "supported on this version of Squashfs\n");
+               return 0;
+       } else
+               return SQUASHFS_INVALID_BLK;
+}
+
+
+static inline int read_xattrs(void *dir_ent)
+{
+       return SQUASHFS_INVALID_XATTR;
+}
+
+
+static inline long long write_xattrs()
+{
+       return SQUASHFS_INVALID_BLK;
+}
+
+
+static inline void save_xattrs()
+{
+}
+
+
+static inline void restore_xattrs()
+{
+}
+
+
+static inline int write_xattr(char *pathname, unsigned int xattr)
+{
+       return 0;
+}
+
+
+static inline int read_xattrs_from_disk(int fd, struct squashfs_super_block *sBlk, int flag, long long *table_start)
+{
+       if(sBlk->xattr_id_table_start != SQUASHFS_INVALID_BLK) {
+               fprintf(stderr, "Xattrs in filesystem! These are not "
+                       "supported on this version of Squashfs\n");
+               return 0;
+       } else
+               return SQUASHFS_INVALID_BLK;
+}
+
+
+static inline struct xattr_list *get_xattr(int i, unsigned int *count, int j)
+{
+       return NULL;
+}
+#endif
+
+#ifdef XATTR_SUPPORT
+#ifdef XATTR_DEFAULT
+#define NOXOPT_STR
+#define XOPT_STR " (default)"
+#define XATTR_DEF 0
+#else
+#define NOXOPT_STR " (default)"
+#define XOPT_STR
+#define XATTR_DEF 1
+#endif
+#else
+#define NOXOPT_STR " (default)"
+#define XOPT_STR " (unsupported)"
+#define XATTR_DEF 1
+#endif
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/xz_wrapper.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/xz_wrapper.c
new file mode 100644 (file)
index 0000000..0977f74
--- /dev/null
@@ -0,0 +1,540 @@
+/*
+ * Copyright (c) 2010, 2011, 2012, 2013
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * xz_wrapper.c
+ *
+ * Support for XZ (LZMA2) compression using XZ Utils liblzma
+ * http://tukaani.org/xz/
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <lzma.h>
+
+#include "squashfs_fs.h"
+#include "xz_wrapper.h"
+#include "compressor.h"
+
+static struct bcj bcj[] = {
+       { "x86", LZMA_FILTER_X86, 0 },
+       { "powerpc", LZMA_FILTER_POWERPC, 0 },
+       { "ia64", LZMA_FILTER_IA64, 0 },
+       { "arm", LZMA_FILTER_ARM, 0 },
+       { "armthumb", LZMA_FILTER_ARMTHUMB, 0 },
+       { "sparc", LZMA_FILTER_SPARC, 0 },
+       { NULL, LZMA_VLI_UNKNOWN, 0 }
+};
+
+static int filter_count = 1;
+static int dictionary_size = 0;
+static float dictionary_percent = 0;
+
+
+/*
+ * This function is called by the options parsing code in mksquashfs.c
+ * to parse any -X compressor option.
+ *
+ * Two specific options are supported:
+ *     -Xbcj
+ *     -Xdict-size
+ *
+ * This function returns:
+ *     >=0 (number of additional args parsed) on success
+ *     -1 if the option was unrecognised, or
+ *     -2 if the option was recognised, but otherwise bad in
+ *        some way (e.g. invalid parameter)
+ *
+ * Note: this function sets internal compressor state, but does not
+ * pass back the results of the parsing other than success/failure.
+ * The xz_dump_options() function is called later to get the options in
+ * a format suitable for writing to the filesystem.
+ */
+static int xz_options(char *argv[], int argc)
+{
+       int i;
+       char *name;
+
+       if(strcmp(argv[0], "-Xbcj") == 0) {
+               if(argc < 2) {
+                       fprintf(stderr, "xz: -Xbcj missing filter\n");
+                       goto failed;
+               }
+
+               name = argv[1];
+               while(name[0] != '\0') {
+                       for(i = 0; bcj[i].name; i++) {
+                               int n = strlen(bcj[i].name);
+                               if((strncmp(name, bcj[i].name, n) == 0) &&
+                                               (name[n] == '\0' ||
+                                                name[n] == ',')) {
+                                       if(bcj[i].selected == 0) {
+                                               bcj[i].selected = 1;
+                                               filter_count++;
+                                       }
+                                       name += name[n] == ',' ? n + 1 : n;
+                                       break;
+                               }
+                       }
+                       if(bcj[i].name == NULL) {
+                               fprintf(stderr, "xz: -Xbcj unrecognised "
+                                       "filter\n");
+                               goto failed;
+                       }
+               }
+       
+               return 1;
+       } else if(strcmp(argv[0], "-Xdict-size") == 0) {
+               char *b;
+               float size;
+
+               if(argc < 2) {
+                       fprintf(stderr, "xz: -Xdict-size missing dict-size\n");
+                       goto failed;
+               }
+
+               size = strtof(argv[1], &b);
+               if(*b == '%') {
+                       if(size <= 0 || size > 100) {
+                               fprintf(stderr, "xz: -Xdict-size percentage "
+                                       "should be 0 < dict-size <= 100\n");
+                               goto failed;
+                       }
+
+                       dictionary_percent = size;
+                       dictionary_size = 0;
+               } else {
+                       if((float) ((int) size) != size) {
+                               fprintf(stderr, "xz: -Xdict-size can't be "
+                                       "fractional unless a percentage of the"
+                                       " block size\n");
+                               goto failed;
+                       }
+
+                       dictionary_percent = 0;
+                       dictionary_size = (int) size;
+
+                       if(*b == 'k' || *b == 'K')
+                               dictionary_size *= 1024;
+                       else if(*b == 'm' || *b == 'M')
+                               dictionary_size *= 1024 * 1024;
+                       else if(*b != '\0') {
+                               fprintf(stderr, "xz: -Xdict-size invalid "
+                                       "dict-size\n");
+                               goto failed;
+                       }
+               }
+
+               return 1;
+       }
+
+       return -1;
+       
+failed:
+       return -2;
+}
+
+
+/*
+ * This function is called after all options have been parsed.
+ * It is used to do post-processing on the compressor options using
+ * values that were not expected to be known at option parse time.
+ *
+ * In this case block_size may not be known until after -Xdict-size has
+ * been processed (in the case where -b is specified after -Xdict-size)
+ *
+ * This function returns 0 on successful post processing, or
+ *                     -1 on error
+ */
+static int xz_options_post(int block_size)
+{
+       /*
+        * if -Xdict-size has been specified use this to compute the datablock
+        * dictionary size
+        */
+       if(dictionary_size || dictionary_percent) {
+               int n;
+
+               if(dictionary_size) {
+                       if(dictionary_size > block_size) {
+                               fprintf(stderr, "xz: -Xdict-size is larger than"
+                               " block_size\n");
+                               goto failed;
+                       }
+               } else
+                       dictionary_size = block_size * dictionary_percent / 100;
+
+               if(dictionary_size < 8192) {
+                       fprintf(stderr, "xz: -Xdict-size should be 8192 bytes "
+                               "or larger\n");
+                       goto failed;
+               }
+
+               /*
+                * dictionary_size must be storable in xz header as either
+                * 2^n or as  2^n+2^(n+1)
+               */
+               n = ffs(dictionary_size) - 1;
+               if(dictionary_size != (1 << n) && 
+                               dictionary_size != ((1 << n) + (1 << (n + 1)))) {
+                       fprintf(stderr, "xz: -Xdict-size is an unsupported "
+                               "value, dict-size must be storable in xz "
+                               "header\n");
+                       fprintf(stderr, "as either 2^n or as 2^n+2^(n+1).  "
+                               "Example dict-sizes are 75%%, 50%%, 37.5%%, "
+                               "25%%,\n");
+                       fprintf(stderr, "or 32K, 16K, 8K etc.\n");
+                       goto failed;
+               }
+
+       } else
+               /* No -Xdict-size specified, use defaults */
+               dictionary_size = block_size;
+
+       return 0;
+
+failed:
+       return -1;
+}
+
+
+/*
+ * This function is called by mksquashfs to dump the parsed
+ * compressor options in a format suitable for writing to the
+ * compressor options field in the filesystem (stored immediately
+ * after the superblock).
+ *
+ * This function returns a pointer to the compression options structure
+ * to be stored (and the size), or NULL if there are no compression
+ * options
+ */
+static void *xz_dump_options(int block_size, int *size)
+{
+       static struct comp_opts comp_opts;
+       int flags = 0, i;
+
+       /*
+        * don't store compressor specific options in file system if the
+        * default options are being used - no compressor options in the
+        * file system means the default options are always assumed
+        *
+        * Defaults are:
+        *  metadata dictionary size: SQUASHFS_METADATA_SIZE
+        *  datablock dictionary size: block_size
+        *  1 filter
+        */
+       if(dictionary_size == block_size && filter_count == 1)
+               return NULL;
+
+       for(i = 0; bcj[i].name; i++)
+               flags |= bcj[i].selected << i;
+
+       comp_opts.dictionary_size = dictionary_size;
+       comp_opts.flags = flags;
+
+       SQUASHFS_INSWAP_COMP_OPTS(&comp_opts);
+
+       *size = sizeof(comp_opts);
+       return &comp_opts;
+}
+
+
+/*
+ * This function is a helper specifically for the append mode of
+ * mksquashfs.  Its purpose is to set the internal compressor state
+ * to the stored compressor options in the passed compressor options
+ * structure.
+ *
+ * In effect this function sets up the compressor options
+ * to the same state they were when the filesystem was originally
+ * generated, this is to ensure on appending, the compressor uses
+ * the same compression options that were used to generate the
+ * original filesystem.
+ *
+ * Note, even if there are no compressor options, this function is still
+ * called with an empty compressor structure (size == 0), to explicitly
+ * set the default options, this is to ensure any user supplied
+ * -X options on the appending mksquashfs command line are over-ridden
+ *
+ * This function returns 0 on sucessful extraction of options, and
+ *                     -1 on error
+ */
+static int xz_extract_options(int block_size, void *buffer, int size)
+{
+       struct comp_opts *comp_opts = buffer;
+       int flags, i, n;
+
+       if(size == 0) {
+               /* set defaults */
+               dictionary_size = block_size;
+               flags = 0;
+       } else {
+               /* check passed comp opts struct is of the correct length */
+               if(size != sizeof(struct comp_opts))
+                       goto failed;
+                                        
+               SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
+
+               dictionary_size = comp_opts->dictionary_size;
+               flags = comp_opts->flags;
+
+               /*
+                * check that the dictionary size seems correct - the dictionary
+                * size should 2^n or 2^n+2^(n+1)
+                */
+               n = ffs(dictionary_size) - 1;
+               if(dictionary_size != (1 << n) && 
+                               dictionary_size != ((1 << n) + (1 << (n + 1))))
+                       goto failed;
+       }
+
+       filter_count = 1;
+       for(i = 0; bcj[i].name; i++) {
+               if((flags >> i) & 1) {
+                       bcj[i].selected = 1;
+                       filter_count ++;
+               } else
+                       bcj[i].selected = 0;
+       }
+
+       return 0;
+
+failed:
+       fprintf(stderr, "xz: error reading stored compressor options from "
+               "filesystem!\n");
+
+       return -1;
+}
+
+
+static void xz_display_options(void *buffer, int size)
+{
+       struct comp_opts *comp_opts = buffer;
+       int dictionary_size, flags, printed;
+       int i, n;
+
+       /* check passed comp opts struct is of the correct length */
+       if(size != sizeof(struct comp_opts))
+               goto failed;
+
+       SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
+
+       dictionary_size = comp_opts->dictionary_size;
+       flags = comp_opts->flags;
+
+       /*
+        * check that the dictionary size seems correct - the dictionary
+        * size should 2^n or 2^n+2^(n+1)
+        */
+       n = ffs(dictionary_size) - 1;
+       if(dictionary_size != (1 << n) && 
+                       dictionary_size != ((1 << n) + (1 << (n + 1))))
+               goto failed;
+
+       printf("\tDictionary size %d\n", dictionary_size);
+
+       printed = 0;
+       for(i = 0; bcj[i].name; i++) {
+               if((flags >> i) & 1) {
+                       if(printed)
+                               printf(", ");
+                       else
+                               printf("\tFilters selected: ");
+                       printf("%s", bcj[i].name);
+                       printed = 1;
+               }
+       }
+
+       if(!printed)
+               printf("\tNo filters specified\n");
+       else
+               printf("\n");
+
+       return;
+
+failed:
+       fprintf(stderr, "xz: error reading stored compressor options from "
+               "filesystem!\n");
+}      
+
+
+/*
+ * This function is called by mksquashfs to initialise the
+ * compressor, before compress() is called.
+ *
+ * This function returns 0 on success, and
+ *                     -1 on error
+ */
+static int xz_init(void **strm, int block_size, int datablock)
+{
+       int i, j, filters = datablock ? filter_count : 1;
+       struct filter *filter = malloc(filters * sizeof(struct filter));
+       struct xz_stream *stream;
+
+       if(filter == NULL)
+               goto failed;
+
+       stream = *strm = malloc(sizeof(struct xz_stream));
+       if(stream == NULL)
+               goto failed2;
+
+       stream->filter = filter;
+       stream->filters = filters;
+
+       memset(filter, 0, filters * sizeof(struct filter));
+
+       stream->dictionary_size = datablock ? dictionary_size :
+               SQUASHFS_METADATA_SIZE;
+
+       filter[0].filter[0].id = LZMA_FILTER_LZMA2;
+       filter[0].filter[0].options = &stream->opt;
+       filter[0].filter[1].id = LZMA_VLI_UNKNOWN;
+
+       for(i = 0, j = 1; datablock && bcj[i].name; i++) {
+               if(bcj[i].selected) {
+                       filter[j].buffer = malloc(block_size);
+                       if(filter[j].buffer == NULL)
+                               goto failed3;
+                       filter[j].filter[0].id = bcj[i].id;
+                       filter[j].filter[1].id = LZMA_FILTER_LZMA2;
+                       filter[j].filter[1].options = &stream->opt;
+                       filter[j].filter[2].id = LZMA_VLI_UNKNOWN;
+                       j++;
+               }
+       }
+
+       return 0;
+
+failed3:
+       for(i = 1; i < filters; i++)
+               free(filter[i].buffer);
+       free(stream);
+
+failed2:
+       free(filter);
+
+failed:
+       return -1;
+}
+
+
+static int xz_compress(void *strm, void *dest, void *src,  int size,
+       int block_size, int *error)
+{
+       int i;
+        lzma_ret res = 0;
+       struct xz_stream *stream = strm;
+       struct filter *selected = NULL;
+
+       stream->filter[0].buffer = dest;
+
+       for(i = 0; i < stream->filters; i++) {
+               struct filter *filter = &stream->filter[i];
+
+               if(lzma_lzma_preset(&stream->opt, LZMA_PRESET_DEFAULT))
+                       goto failed;
+
+               stream->opt.dict_size = stream->dictionary_size;
+
+               filter->length = 0;
+               res = lzma_stream_buffer_encode(filter->filter,
+                       LZMA_CHECK_CRC32, NULL, src, size, filter->buffer,
+                       &filter->length, block_size);
+       
+               if(res == LZMA_OK) {
+                       if(!selected || selected->length > filter->length)
+                               selected = filter;
+               } else if(res != LZMA_BUF_ERROR)
+                       goto failed;
+       }
+
+       if(!selected)
+               /*
+                * Output buffer overflow.  Return out of buffer space
+                */
+               return 0;
+
+       if(selected->buffer != dest)
+               memcpy(dest, selected->buffer, selected->length);
+
+       return (int) selected->length;
+
+failed:
+       /*
+        * All other errors return failure, with the compressor
+        * specific error code in *error
+        */
+       *error = res;
+       return -1;
+}
+
+
+static int xz_uncompress(void *dest, void *src, int size, int outsize,
+       int *error)
+{
+       size_t src_pos = 0;
+       size_t dest_pos = 0;
+       uint64_t memlimit = MEMLIMIT;
+
+       lzma_ret res = lzma_stream_buffer_decode(&memlimit, 0, NULL,
+                       src, &src_pos, size, dest, &dest_pos, outsize);
+
+       if(res == LZMA_OK && size == (int) src_pos)
+               return (int) dest_pos;
+       else {
+               *error = res;
+               return -1;
+       }
+}
+
+
+static void xz_usage()
+{
+       fprintf(stderr, "\t  -Xbcj filter1,filter2,...,filterN\n");
+       fprintf(stderr, "\t\tCompress using filter1,filter2,...,filterN in");
+       fprintf(stderr, " turn\n\t\t(in addition to no filter), and choose");
+       fprintf(stderr, " the best compression.\n");
+       fprintf(stderr, "\t\tAvailable filters: x86, arm, armthumb,");
+       fprintf(stderr, " powerpc, sparc, ia64\n");
+       fprintf(stderr, "\t  -Xdict-size <dict-size>\n");
+       fprintf(stderr, "\t\tUse <dict-size> as the XZ dictionary size.  The");
+       fprintf(stderr, " dictionary size\n\t\tcan be specified as a");
+       fprintf(stderr, " percentage of the block size, or as an\n\t\t");
+       fprintf(stderr, "absolute value.  The dictionary size must be less");
+       fprintf(stderr, " than or equal\n\t\tto the block size and 8192 bytes");
+       fprintf(stderr, " or larger.  It must also be\n\t\tstorable in the xz");
+       fprintf(stderr, " header as either 2^n or as 2^n+2^(n+1).\n\t\t");
+       fprintf(stderr, "Example dict-sizes are 75%%, 50%%, 37.5%%, 25%%, or");
+       fprintf(stderr, " 32K, 16K, 8K\n\t\tetc.\n");
+}
+
+
+struct compressor xz_comp_ops = {
+       .init = xz_init,
+       .compress = xz_compress,
+       .uncompress = xz_uncompress,
+       .options = xz_options,
+       .options_post = xz_options_post,
+       .dump_options = xz_dump_options,
+       .extract_options = xz_extract_options,
+       .display_options = xz_display_options,
+       .usage = xz_usage,
+       .id = XZ_COMPRESSION,
+       .name = "xz",
+       .supported = 1
+};
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/xz_wrapper.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/xz_wrapper.h
new file mode 100644 (file)
index 0000000..ce2545c
--- /dev/null
@@ -0,0 +1,71 @@
+#ifndef XZ_WRAPPER_H
+#define XZ_WRAPPER_H
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2010
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+ *
+ * xz_wrapper.h
+ *
+ */
+
+#ifndef linux
+#define __BYTE_ORDER BYTE_ORDER
+#define __BIG_ENDIAN BIG_ENDIAN
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#else
+#include <endian.h>
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+extern unsigned int inswap_le32(unsigned int);
+
+#define SQUASHFS_INSWAP_COMP_OPTS(s) { \
+       (s)->dictionary_size = inswap_le32((s)->dictionary_size); \
+       (s)->flags = inswap_le32((s)->flags); \
+}
+#else
+#define SQUASHFS_INSWAP_COMP_OPTS(s)
+#endif
+
+#define MEMLIMIT (32 * 1024 * 1024)
+
+struct bcj {
+       char            *name;
+       lzma_vli        id;
+       int             selected;
+};
+
+struct filter {
+       void            *buffer;
+       lzma_filter     filter[3];
+       size_t          length;
+};
+
+struct xz_stream {
+       struct filter   *filter;
+       int             filters;
+       int             dictionary_size;
+       lzma_options_lzma opt;
+};
+
+struct comp_opts {
+       int dictionary_size;
+       int flags;
+};
+#endif
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/zstd_wrapper.c b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/zstd_wrapper.c
new file mode 100644 (file)
index 0000000..f37b7f4
--- /dev/null
@@ -0,0 +1,199 @@
+/*
+ * Copyright (c) 2017
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * zstd_wrapper.c
+ *
+ * Support for ZSTD compression http://zstd.net
+ */
+
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <zstd.h>
+#include <zstd_errors.h>
+
+#include "squashfs_fs.h"
+#include "zstd_wrapper.h"
+#include "compressor.h"
+
+static int compression_level = ZSTD_DEFAULT_COMPRESSION_LEVEL;
+
+/*
+ * This function is called by the options parsing code in mksquashfs.c
+ * to parse any -X compressor option.
+ *
+ * This function returns:
+ *     >=0 (number of additional args parsed) on success
+ *     -1 if the option was unrecognised, or
+ *     -2 if the option was recognised, but otherwise bad in
+ *        some way (e.g. invalid parameter)
+ *
+ * Note: this function sets internal compressor state, but does not
+ * pass back the results of the parsing other than success/failure.
+ * The zstd_dump_options() function is called later to get the options in
+ * a format suitable for writing to the filesystem.
+ */
+static int zstd_options(char *argv[], int argc)
+{
+       return 1;
+}
+
+/*
+ * This function is called by mksquashfs to dump the parsed
+ * compressor options in a format suitable for writing to the
+ * compressor options field in the filesystem (stored immediately
+ * after the superblock).
+ *
+ * This function returns a pointer to the compression options structure
+ * to be stored (and the size), or NULL if there are no compression
+ * options.
+ */
+static void *zstd_dump_options(int block_size, int *size)
+{
+       return NULL;
+}
+
+/*
+ * This function is a helper specifically for the append mode of
+ * mksquashfs.  Its purpose is to set the internal compressor state
+ * to the stored compressor options in the passed compressor options
+ * structure.
+ *
+ * In effect this function sets up the compressor options
+ * to the same state they were when the filesystem was originally
+ * generated, this is to ensure on appending, the compressor uses
+ * the same compression options that were used to generate the
+ * original filesystem.
+ *
+ * Note, even if there are no compressor options, this function is still
+ * called with an empty compressor structure (size == 0), to explicitly
+ * set the default options, this is to ensure any user supplied
+ * -X options on the appending mksquashfs command line are over-ridden.
+ *
+ * This function returns 0 on sucessful extraction of options, and -1 on error.
+ */
+static int zstd_extract_options(int block_size, void *buffer, int size)
+{
+       struct zstd_comp_opts *comp_opts = buffer;
+
+       if (size == 0) {
+               /* Set default values */
+               compression_level = ZSTD_DEFAULT_COMPRESSION_LEVEL;
+               return 0;
+       }
+
+       /* we expect a comp_opts structure of sufficient size to be present */
+       if (size < sizeof(*comp_opts))
+               goto failed;
+
+       SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
+
+       if (comp_opts->compression_level < 1) {
+               fprintf(stderr, "zstd: bad compression level in compression "
+                       "options structure\n");
+               goto failed;
+       }
+
+       compression_level = comp_opts->compression_level;
+
+       return 0;
+
+failed:
+       fprintf(stderr, "zstd: error reading stored compressor options from "
+               "filesystem!\n");
+
+       return -1;
+}
+
+static void zstd_display_options(void *buffer, int size)
+{
+       struct zstd_comp_opts *comp_opts = buffer;
+
+       /* we expect a comp_opts structure of sufficient size to be present */
+       if (size < sizeof(*comp_opts))
+               goto failed;
+
+       SQUASHFS_INSWAP_COMP_OPTS(comp_opts);
+
+       if (comp_opts->compression_level < 1) {
+               fprintf(stderr, "zstd: bad compression level in compression "
+                       "options structure\n");
+               goto failed;
+       }
+
+       printf("\tcompression-level %d\n", comp_opts->compression_level);
+
+       return;
+
+failed:
+       fprintf(stderr, "zstd: error reading stored compressor options from "
+               "filesystem!\n");
+}
+
+/*
+ * This function is called by mksquashfs to initialise the
+ * compressor, before compress() is called.
+ *
+ * This function returns 0 on success, and -1 on error.
+ */
+static int zstd_init(void **strm, int block_size, int datablock)
+{
+       return 0;
+}
+
+static int zstd_compress(void *strm, void *dest, void *src, int size,
+                        int block_size, int *error)
+{
+    (void)strm;
+    (void)dest;
+    (void)src;
+    (void)size;
+    (void)block_size;
+    (void)error;
+       return 0;
+}
+
+static int zstd_uncompress(void *dest, void *src, int size, int outsize,
+                          int *error)
+{
+       const size_t res = ZSTD_decompress(dest, outsize, src, size);
+
+       if (ZSTD_isError(res)) {
+               fprintf(stderr, "\t%d %d\n", outsize, size);
+
+               *error = (int)ZSTD_getErrorCode(res);
+               return -1;
+       }
+
+       return (int)res;
+}
+
+static void zstd_usage(void)
+{
+       fprintf(stderr, "\t  -Xcompression-level <compression-level>\n");
+}
+
+struct compressor zstd_comp_ops = {
+       .init = zstd_init,
+       .compress = zstd_compress,
+       .uncompress = zstd_uncompress,
+       .options = zstd_options,
+       .dump_options = zstd_dump_options,
+       .extract_options = zstd_extract_options,
+       .display_options = zstd_display_options,
+       .usage = zstd_usage,
+       .id = ZSTD_COMPRESSION,
+       .name = "zstd",
+       .supported = 1
+};
diff --git a/SQUASHFS/squashfs-tools-4.4/squashfs-tools/zstd_wrapper.h b/SQUASHFS/squashfs-tools-4.4/squashfs-tools/zstd_wrapper.h
new file mode 100644 (file)
index 0000000..4fbef0a
--- /dev/null
@@ -0,0 +1,48 @@
+#ifndef ZSTD_WRAPPER_H
+#define ZSTD_WRAPPER_H
+/*
+ * Squashfs
+ *
+ * Copyright (c) 2017
+ * Phillip Lougher <phillip@squashfs.org.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2,
+ * or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * zstd_wrapper.h
+ *
+ */
+
+#ifndef linux
+#define __BYTE_ORDER BYTE_ORDER
+#define __BIG_ENDIAN BIG_ENDIAN
+#define __LITTLE_ENDIAN LITTLE_ENDIAN
+#else
+#include <endian.h>
+#endif
+
+#if __BYTE_ORDER == __BIG_ENDIAN
+extern unsigned int inswap_le16(unsigned short);
+extern unsigned int inswap_le32(unsigned int);
+
+#define SQUASHFS_INSWAP_COMP_OPTS(s) { \
+       (s)->compression_level = inswap_le32((s)->compression_level); \
+}
+#else
+#define SQUASHFS_INSWAP_COMP_OPTS(s)
+#endif
+
+/* Default compression */
+#define ZSTD_DEFAULT_COMPRESSION_LEVEL 15
+
+struct zstd_comp_opts {
+       int compression_level;
+};
+#endif
diff --git a/VBLADE/vblade-master/.gitignore b/VBLADE/vblade-master/.gitignore
new file mode 100644 (file)
index 0000000..36a74e1
--- /dev/null
@@ -0,0 +1,5 @@
+*.orig
+cscope.*
+*.rej
+*~
+*.o
diff --git a/VBLADE/vblade-master/COPYING b/VBLADE/vblade-master/COPYING
new file mode 100644 (file)
index 0000000..d60c31a
--- /dev/null
@@ -0,0 +1,340 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+     59 Temple Place, Suite 330, Boston, MA  02111-1307  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.
+\f
+                   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.)
+\f
+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.
+\f
+  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.
+\f
+  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
+\f
+           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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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.
+
+  <signature of Ty Coon>, 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.
diff --git a/VBLADE/vblade-master/HACKING b/VBLADE/vblade-master/HACKING
new file mode 100644 (file)
index 0000000..2146712
--- /dev/null
@@ -0,0 +1,36 @@
+Contributions to the vblade are welcome.
+
+In contributing, though, please stay true to the original simplicity
+of the software.  Many open source projects suffer from the "creeping
+feature demon" phenomenon.  If you think the vblade needs a great new
+feature, first seriously try to think of a way to accomplish your goal
+without adding to the vblade itself.
+
+Patches should be clean (to the point and easy to read) and should do
+one thing.  (Avoid, for example, mixing style changes with substantive
+changes.)  Send multiple patches if necessary.  Patches should be
+generated with "diff -uprN" if possible, and should be designed to be
+applied with "patch -p1".
+
+When possible, the best way to submit a patch is by sending it to the
+aoetools-discuss list.  You can subscribe at the aoetools project web
+page on sourceforge.net.
+
+When you send your patch, here are some things to cover:
+
+  * What version of the vblade did you use to generate the patch?
+    (Hopefully it was the latest.)
+
+  * What was your motivation for creating the patch?  That is, what
+    problem does it solve?
+
+  * What testing did you perform to ensure that your patch did not
+    introduce bugs and accomplished what you intended?
+
+  * If your changes affect the end-user experience, have you updated
+    the vblade documentation?
+
+  * Is your email client able to send a patch without changing it?
+    Many email clients and servers corrupt patches.  Please test your
+    email chain by sending an applying a patch before sending your
+    patch to the mailing list.
diff --git a/VBLADE/vblade-master/NEWS b/VBLADE/vblade-master/NEWS
new file mode 100644 (file)
index 0000000..5caf8ff
--- /dev/null
@@ -0,0 +1,155 @@
+-*- change-log -*-
+2018-08-25 Christoph Biedl <sourceforge.bnwi@manchmal.in-ulm.de>
+       Print helpful error and exit immediately for missing device
+       vblade-24
+
+2017-11-19 Catalin Salgau <csalgau@users.sourceforge.net>
+       On FreeBSD limit used MTU to address BPF limitation
+
+2015-06-14 Ed Cashin <ed.cashin@acm.org>
+       Add convenience script for creating sparse files
+       vblade-23
+
+2015-02-25 Catalin Salgau <csalgau@users.sourceforge.net>
+       Warn about Windows problem with CHS misalignment
+
+2014-09-13 Ed Cashin <ed.cashin@acm.org>
+       code cleanup: remove unused variables
+
+2014-08-10 Ed Cashin <ed.cashin@acm.org>
+       vblade-22
+
+2014-06-08 Ed Cashin <ed.cashin@acm.org>
+       update version for v22 release candidate 1
+       buffer boundary cleanups
+       FreeBSD BPF and MTU fixes from Catalin Salgau
+       offset and size options by Christoph Biedl
+       vblade-22rc1
+
+2013-03-18 Ed Cashin <ecashin@coraid.com>
+       add big-endian support from Daniel Mealha Cabrita <dancab@gmx.net>
+       vblade-21
+
+2009-08-14 Sam Hopkins <sah@coraid.com>
+       bugfix: aoe command error did not set Error bit in flags
+       add support for AoEr11
+       set ident serial to shelf.slot:hostname
+       vblade-20
+
+2008-10-08 Ed Cashin <ecashin@coraid.com>
+       add Chris Webb's bpf fix for FreeBSD
+       add Ryan Thomas's fix to stop bufcnt being overridden
+       vblade-19
+       
+2008-07-14 Ed Cashin <ecashin@coraid.com>
+       add Chris Webb's block device options patch
+       add Chris Webb's socket options patch for better jumbo handling
+       remove obsolete contrib/o_direct.diff
+       vblade-18
+
+2008-06-09 Ed Cashin <ecashin@coraid.com>
+       add Chris Webb's latest BPF patch to vblade, remove from contrib
+       update contributed AIO patch for compatibility with current sources
+       vblade-17
+       
+2008-05-07 Ed Cashin <ecashin@coraid.com>
+       add Chris Webb's AIO patch to the contributions
+       add Chris Webb's BPF patch to the contributions
+       vblade-16
+
+2008-02-20 Ed Cashin <ecashin@coraid.com>
+       require the amount of data we use, not the amount ethernet requires
+       make sure the packet length agrees with the config query length
+       make sure the packet length agrees with the amount to write
+       remove newline embedded in fw version field of ATA dev ID response
+       vblade-15
+       
+2006-11-20 Sam Hopkins <sah@coraid.com>
+       apply contrib jumbo patch to standard distribution
+       add jumbo configuration app. note in README
+       add jumbo README reference to manpage
+       add mask feature; -m flag
+       update manpage to describe -m flag
+       vblade-14
+
+2006-10-05 Sam Hopkins <sah@coraid.com>
+       fix confcmd memcpy bug
+       correct scnt return value in read/write ata response
+       replace O_RDONLY fallback with explicit stat.  root always wins.
+       vblade-13
+
+2006-10-04 Sam Hopkins <sah@coraid.com>
+       fix confcmd buglets
+       fix atacmd buglets
+       add atacmd handling for bad argument errors
+       add O_RDONLY open if O_RDWR fails
+       add contrib patch directory
+       add contrib/README
+       add jumbo patch to contrib
+       add o_direct patch to contrib
+       vblade-12
+
+2006-09-21 "Adam J. Richter" <adam@yggdrasil.com>
+       add install target for makefile
+       vblade-11
+       
+2005-12-06 Ed Cashin <ecashin@coraid.com>
+       fix u64 configuration on FreeBSD
+       release vblade-10
+       
+2005-12-06 Valeriy Glushkov <valery@rocketdivision.com>
+       implemented config string support
+       added handler for ATA Check power mode command
+       
+2005-11-15 Ed Cashin <ecashin@coraid.com>
+       add compatibility with platforms lacking u64 (e.g., Slackware)
+       release vblade-9
+       
+2005-11-10 Ed Cashin <ecashin@coraid.com>
+       call atainit on program startup
+       put VBLADE_VERSION in dat.h and use it in firmware version
+       release vblade-7
+       include Stacey's patch to use p{read,write} on FreeBSD
+       include Stacey's patch to typedef ulong on FreeBSD
+       fix makefile dependencies (e.g., rebuild on new aoe.c)
+       fix config string length specification
+       include Stacey's patch to avoid compile warnings on FreeBSD
+       release vblade-8
+       
+2005-11-10 "Stacey D. Son" <sson@verio.net>
+       include FreeBSD support
+       
+2005-10-03 Ed Cashin <ecashin@coraid.com>
+       don't invoke vblade with dash from vbladed
+       
+2005-08-31 20:14:12 GMT Ed Cashin <ecashin@coraid.com>
+       ATA identify: don't juggle bytes in shorts on big endian arch
+       add manpage for vblade, vbladed
+       release vblade-6
+       
+2005-03-17 15:24:30 GMT        Ed Cashin <ecashin@coraid.com>
+       follow up on vblade-2's off-by-one patch, making end of device usable
+       release vblade-5
+       
+2005-03-15 22:03:17 GMT        Ed Cashin <ecashin@coraid.com>
+       don't rely on kernel headers for defining the aoe type 0x88a2
+       release vblade-4
+       
+2005-03-15 17:27:01 GMT        Ed Cashin <ecashin@coraid.com>
+       docs: aoe-2.6-7 is the first driver to support multiple blades per mac
+       release vblade-3
+       
+2005-03-11 18:30:26 GMT        Ed Cashin <ecashin@coraid.com>
+       put 64-bit configuration into config.h file
+       don't use uninitialized variables
+       broadcast config query on startup
+       clarify desired patch format in HACKING
+       add sah@coraid.com's vblade-1.ata.c.patch: fix off-by-one and ext LBA
+       add docs, remove daemonizing code from vblade
+       release vblade-2
+       
+2005-02-08 20:21:52 GMT        Ed Cashin <ecashin@coraid.com>
+       starting documentation
+       add script that daemonizes vblade process, logging output
+       make vblade sources -Wall clean, use daemon(3)
+       release vblade-1
diff --git a/VBLADE/vblade-master/README b/VBLADE/vblade-master/README
new file mode 100644 (file)
index 0000000..72799a8
--- /dev/null
@@ -0,0 +1,146 @@
+
+INTRODUCTION
+------------
+
+The vblade is a minimal ATA over Ethernet (AoE) storage target.  Its
+focus is simplicity, not performance or richness of features.  It
+exports a seekable file available over an ethernet local area network
+(LAN) via the AoE data storage protocol.
+
+The name, "vblade," is historical: It is a virtual EtherDrive (R)
+blade.  The first AoE target hardware sold by Coraid was in a blade
+form factor, ten to a 4-rack-unit chassis.
+
+The seekable file is typically a block device like /dev/md0 but even
+regular files will work.  Sparse files can be especially convenient.
+When vblade exports the block storage over AoE it becomes a storage
+target.  Another host on the same LAN can access the storage if it has
+a compatible aoe kernel driver.
+
+BUILDING
+--------
+
+The following command should build the vblade program on a Linux-based
+system:
+
+  make
+
+For FreeBSD systems, include an extra parameter like so:
+
+  make PLATFORM=freebsd
+
+EXAMPLES
+--------
+
+There is a "vbladed" script that daemonizes the program and sends its
+output to the logger program.  Make sure you have logger installed if
+you would like to run vblade as a daemon with the vbladed script.
+
+  ecashin@kokone vblade$ echo 'I have logger' | logger
+  ecashin@kokone vblade$ tail -3 /var/log/messages
+  Feb  8 14:52:49 kokone -- MARK --
+  Feb  8 15:12:49 kokone -- MARK --
+  Feb  8 15:19:56 kokone logger: I have logger
+
+Here is a short example showing how to export a block device with a
+vblade.  (This is a loop device backed by a sparse file, but you could
+use any seekable file instead of /dev/loop7.)
+
+  ecashin@kokone vblade$ make
+  cc -Wall   -c -o aoe.o aoe.c
+  cc -Wall   -c -o linux.o linux.c
+  cc -Wall   -c -o ata.o ata.c
+  cc -o vblade aoe.o linux.o ata.o
+  ecashin@kokone vblade$ su
+  Password: 
+  root@kokone vblade# modprobe loop
+  root@kokone vblade# dd if=/dev/zero bs=1k count=1 seek=`expr 1024 \* 4096` of=bd
+  -file
+  1+0 records in
+  1+0 records out
+  1024 bytes transferred in 0.009901 seconds (103423 bytes/sec)
+  root@kokone vblade# losetup /dev/loop7 bd-file  
+  root@kokone vblade# ./vblade 9 0 eth0 /dev/loop7 
+  ioctl returned 0
+  4294968320 bytes
+  pid 16967: e9.0, 8388610 sectors
+
+Here's how you can use the Linux aoe driver to access the storage from
+another host on the LAN.
+
+  ecashin@kokone ecashin$ ssh makki
+  Last login: Mon Feb  7 10:25:04 2005
+  ecashin@makki ~$ su
+  Password: 
+  root@makki ecashin# modprobe aoe
+  root@makki ecashin# aoe-stat
+      e9.0            eth1              up
+  root@makki ecashin# mkfs -t ext3 /dev/etherd/e9.0
+  mke2fs 1.35 (28-Feb-2004)
+...
+  Creating journal (8192 blocks): done
+  Writing superblocks and filesystem accounting information: done
+  
+  This filesystem will be automatically checked every 24 mounts or
+  180 days, whichever comes first.  Use tune2fs -c or -i to override.
+  root@makki ecashin# mkdir /mnt/e9.0
+  root@makki ecashin# mount /dev/etherd/e9.0 /mnt/e9.0
+  root@makki ecashin# echo hooray > /mnt/e9.0/test.txt
+  root@makki ecashin# cat /mnt/e9.0/test.txt
+  hooray
+
+Remember: be as careful with these devices as you would with /dev/hda!
+
+Jumbo Frame Compatibility
+-------------------------
+
+Vblade can use jumbo frames provided your initiator is jumbo frame
+capable.  There is one small configuration gotcha to consider 
+to avoid having the vblade kernel frequently drop frames.
+
+Vblade uses a raw socket to perform AoE.  The linux kernel will
+only buffer a certain amount of data for a raw socket.  For 2.6
+kernels, this value is managed through /proc:
+
+root@nai aoe# grep . /proc/sys/net/core/rmem_*
+/proc/sys/net/core/rmem_default:128000
+/proc/sys/net/core/rmem_max:128000
+
+rmem_max is the max amount a user process may expand the receive
+buffer to -- through setsockopt(...) -- and rmem_default is, as you
+might expect, the default.
+
+The gotcha is that this amount to buffer does not relate
+to the amount of user data buffered, but the amount of
+real data buffered.  As an example, the Intel GbE controller
+must be given 16KB frames to use an MTU over 8KB.
+For each received frame, the kernel must be able to buffer
+16KB, even if the aoe frame is only 60 bytes in length.
+
+The linux aoe initiator will use 16 outstanding frames when
+used with vblade.  A good default for ensuring frames are
+not dropped is to allocate 16KB for 17 frames:
+
+for f in /proc/sys/net/core/rmem_*; do echo $((17 * 16 * 1024)) >$f; done
+
+Be sure to start vblade after changing the buffering defaults
+as the buffer value is set when the socket is opened.
+
+AoE Initiator Compatibility
+---------------------------
+
+The Linux aoe driver for the 2.6 kernel is compatible if you use
+aoe-2.6-7 or newer.  You can use older aoe drivers but you will only
+be able to see one vblade per MAC address.
+
+Contrib Patches
+---------------
+
+see contrib/README
+
+Kvblade
+-------
+
+  While vblade runs as a userland process (like "ls" or "vi"), there
+  is another program that runs inside the kernel.  It is called
+  kvblade.  It is alpha software.
diff --git a/VBLADE/vblade-master/aoe.c b/VBLADE/vblade-master/aoe.c
new file mode 100644 (file)
index 0000000..9fd7ed5
--- /dev/null
@@ -0,0 +1,740 @@
+// aoe.c: the ATA over Ethernet virtual EtherDrive (R) blade
+#define _GNU_SOURCE
+#include "config.h"
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <netinet/in.h>
+#include "dat.h"
+#include "fns.h"
+
+enum {
+       Nmasks= 32,
+       Nsrr= 256,
+       Alen= 6,
+};
+
+uchar masks[Nmasks*Alen];
+int nmasks;
+uchar srr[Nsrr*Alen];
+int nsrr;
+char config[Nconfig];
+int nconfig = 0;
+int maxscnt = 2;
+char *ifname;
+int bufcnt = Bufcount;
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+typedef unsigned long long u64_t;
+typedef unsigned int    u32_t;
+
+#pragma pack(4)
+typedef struct ventoy_img_chunk
+{
+    u32_t img_start_sector; // sector size: 2KB
+    u32_t img_end_sector;   // included
+
+    u64_t disk_start_sector; // in disk_sector_size
+    u64_t disk_end_sector;   // included
+}ventoy_img_chunk;
+
+typedef struct ventoy_disk_map
+{
+    u64_t img_start_sector;
+    u64_t img_end_sector;
+    u64_t disk_start_sector;
+    u64_t disk_end_sector;
+}ventoy_disk_map;
+#pragma pack()
+
+static int verbose = 0;
+static u64_t g_iso_file_size = 0;
+static int g_img_map_num = 0;
+static ventoy_disk_map *g_img_map = NULL;
+
+static ventoy_disk_map * vtoydm_get_img_map_data(const char *img_map_file, int *plen)
+{
+    int i;
+    int len;
+    int rc = 1;
+    u64_t sector_num;
+    FILE *fp = NULL;
+    ventoy_img_chunk *chunk = NULL;
+    ventoy_disk_map *map = NULL;
+    
+    fp = fopen(img_map_file, "rb");
+    if (NULL == fp)
+    {
+        fprintf(stderr, "Failed to open file %s\n", img_map_file);
+        return NULL;
+    }
+
+    fseek(fp, 0, SEEK_END);
+    len = (int)ftell(fp);
+    fseek(fp, 0, SEEK_SET);
+
+    chunk = (ventoy_img_chunk *)malloc(len);
+    if (NULL == chunk)
+    {
+        fprintf(stderr, "Failed to malloc memory len:%d\n", len);
+        goto end;
+    }
+
+    if (fread(chunk, 1, len, fp) != len)
+    {
+        fprintf(stderr, "Failed to read file\n");
+        goto end;
+    }
+
+    if (len % sizeof(ventoy_img_chunk))
+    {
+        fprintf(stderr, "image map file size %d is not aligned with %d\n", 
+                len, (int)sizeof(ventoy_img_chunk));
+        goto end;
+    }
+
+    map = (ventoy_disk_map *)malloc((len / sizeof(ventoy_img_chunk)) * sizeof(ventoy_disk_map));
+    if (NULL == map)
+    {
+        fprintf(stderr, "Failed to malloc memory\n");
+        goto end;
+    }
+    
+    for (i = 0; i < len / sizeof(ventoy_img_chunk); i++)
+    {
+        sector_num = chunk[i].img_end_sector - chunk[i].img_start_sector + 1;
+        g_iso_file_size += sector_num * 2048;
+        
+        map[i].img_start_sector = chunk[i].img_start_sector << 2;
+        map[i].img_end_sector = (chunk[i].img_end_sector << 2) + 3;
+        map[i].disk_start_sector = chunk[i].disk_start_sector;
+        map[i].disk_end_sector = chunk[i].disk_end_sector;
+    }
+
+    rc = 0;
+end:
+    fclose(fp);
+
+    if (chunk)
+    {
+        free(chunk);
+        chunk = NULL;
+    }
+
+    *plen = len;
+    return map;
+}
+
+static void parse_img_chunk(const char *img_map_file)
+{
+    int len;
+
+    g_img_map = vtoydm_get_img_map_data(img_map_file, &len);
+    if (g_img_map)
+    {
+        g_img_map_num = len / sizeof(ventoy_img_chunk);
+    }
+}
+
+static u64_t get_disk_sector(u64_t lba)
+{
+    int i;
+    ventoy_disk_map *cur = g_img_map;
+    
+    for (i = 0; i < g_img_map_num; i++, cur++)
+    {
+        if (lba >= cur->img_start_sector && lba <= cur->img_end_sector)
+        {
+            return (lba - cur->img_start_sector) + cur->disk_start_sector;
+        }
+    }
+
+    return 0;
+}
+
+int getsec(int fd, uchar *place, vlong lba, int nsec)
+{
+    int i;
+    int count = 0;
+    u64_t last_sector;
+    u64_t sector;
+
+    count = 1;
+    last_sector = get_disk_sector((u64_t)lba);
+    
+    for (i = 1; i < nsec; i++)
+    {
+        sector = get_disk_sector((u64_t)(lba + i));
+        if (sector == (last_sector + count))
+        {
+            count++;
+        }
+        else
+        {
+            lseek(fd, last_sector * 512, SEEK_SET);
+            read(fd, place, count * 512);
+
+            last_sector = sector;
+            count = 1;
+        }
+    }
+
+    lseek(fd, last_sector * 512, SEEK_SET);
+    read(fd, place, count * 512);
+
+       return nsec * 512;
+}
+// read only
+int putsec(int fd, uchar *place, vlong lba, int nsec)
+{
+    return nsec * 512;
+}
+
+
+void
+aoead(int fd)  // advertise the virtual blade
+{
+       uchar buf[2000];
+       Conf *p;
+       int i;
+
+       p = (Conf *)buf;
+       memset(p, 0, sizeof *p);
+       memset(p->h.dst, 0xff, 6);
+       memmove(p->h.src, mac, 6);
+       p->h.type = htons(0x88a2);
+       p->h.flags = Resp;
+       p->h.maj = htons(shelf);
+       p->h.min = slot;
+       p->h.cmd = Config;
+       p->bufcnt = htons(bufcnt);
+       p->scnt = maxscnt = (getmtu(sfd, ifname) - sizeof (Ata)) / 512;
+       p->firmware = htons(FWV);
+       p->vercmd = 0x10 | Qread;
+       memcpy(p->data, config, nconfig);
+       p->len = htons(nconfig);
+       if (nmasks == 0)
+       if (putpkt(fd, buf, sizeof *p - sizeof p->data + nconfig) == -1) {
+               perror("putpkt aoe id");
+               return;
+       }
+       for (i=0; i<nmasks; i++) {
+               memcpy(p->h.dst, &masks[i*Alen], Alen);
+               if (putpkt(fd, buf, sizeof *p - sizeof p->data + nconfig) == -1)
+                       perror("putpkt aoe id");
+       }
+}
+
+int
+isbcast(uchar *ea)
+{
+       uchar *b = (uchar *)"\377\377\377\377\377\377";
+
+       return memcmp(ea, b, 6) == 0;
+}
+
+long long
+getlba(uchar *p)
+{
+       vlong v;
+       int i;
+
+       v = 0;
+       for (i = 0; i < 6; i++)
+               v |= (vlong)(*p++) << i * 8;
+       return v;
+}
+
+int
+aoeata(Ata *p, int pktlen)     // do ATA reqeust
+{
+       Ataregs r;
+       int len = 60;
+       int n;
+
+       r.lba = getlba(p->lba);
+       r.sectors = p->sectors;
+       r.feature = p->err;
+       r.cmd = p->cmd;
+       if (r.cmd != 0xec)
+       if (!rrok(p->h.src)) {
+               p->h.flags |= Error;
+               p->h.error = Res;
+               return len;
+       }
+       if (atacmd(&r, (uchar *)(p+1), maxscnt*512, pktlen - sizeof(*p)) < 0) {
+               p->h.flags |= Error;
+               p->h.error = BadArg;
+               return len;
+       }
+       if (!(p->aflag & Write))
+       if ((n = p->sectors)) {
+               n -= r.sectors;
+               len = sizeof (Ata) + (n*512);
+       }
+       p->sectors = r.sectors;
+       p->err = r.err;
+       p->cmd = r.status;
+       return len;
+}
+
+#define QCMD(x) ((x)->vercmd & 0xf)
+
+// yes, this makes unnecessary copies.
+
+int
+confcmd(Conf *p, int payload)  // process conf request
+{
+       int len;
+
+       len = ntohs(p->len);
+       if (QCMD(p) != Qread)
+       if (len > Nconfig || len > payload)
+               return 0;       // if you can't play nice ...
+       switch (QCMD(p)) {
+       case Qtest:
+               if (len != nconfig)
+                       return 0;
+               // fall thru
+       case Qprefix:
+               if (len > nconfig)
+                       return 0;
+               if (memcmp(config, p->data, len))
+                       return 0;
+               // fall thru
+       case Qread:
+               break;
+       case Qset:
+               if (nconfig)
+               if (nconfig != len || memcmp(config, p->data, len)) {
+                       p->h.flags |= Error;
+                       p->h.error = ConfigErr;
+                       break;
+               }
+               // fall thru
+       case Qfset:
+               nconfig = len;
+               memcpy(config, p->data, nconfig);
+               break;
+       default:
+               p->h.flags |= Error;
+               p->h.error = BadArg;
+       }
+       memmove(p->data, config, nconfig);
+       p->len = htons(nconfig);
+       p->bufcnt = htons(bufcnt);
+       p->scnt = maxscnt = (getmtu(sfd, ifname) - sizeof (Ata)) / 512;
+       p->firmware = htons(FWV);
+       p->vercmd = 0x10 | QCMD(p);     // aoe v.1
+       return nconfig + sizeof *p - sizeof p->data;
+}
+
+static int
+aoesrr(Aoesrr *sh, int len)
+{
+       uchar *m, *e;
+       int n;
+
+       e = (uchar *) sh + len;
+       m = (uchar *) sh + Nsrrhdr;
+       switch (sh->rcmd) {
+       default:
+e:             sh->h.error = BadArg;
+               sh->h.flags |= Error;
+               break;
+       case 1: // set
+               if (!rrok(sh->h.src)) {
+                       sh->h.error = Res;
+                       sh->h.flags |= Error;
+                       break;
+               }
+       case 2: // force set
+               n = sh->nmacs * 6;
+               if (e < m + n)
+                       goto e;
+               nsrr = sh->nmacs;
+               memmove(srr, m, n);
+       case 0: // read
+               break;
+       }
+       sh->nmacs = nsrr;
+       n = nsrr * 6;
+       memmove(m, srr, n);
+       return Nsrrhdr + n;
+}
+
+static int
+addmask(uchar *ea)
+{
+
+       uchar *p, *e;
+
+       p = masks;
+       e = p + nmasks;
+       for (; p<e; p += 6)
+               if (!memcmp(p, ea, 6))
+                       return 2;
+       if (nmasks >= Nmasks)
+               return 0;
+       memmove(p, ea, 6);
+       nmasks++;
+       return 1;
+}
+
+static void
+rmmask(uchar *ea)
+{
+       uchar *p, *e;
+
+       p = masks;
+       e = p + nmasks;
+       for (; p<e; p+=6)
+               if (!memcmp(p, ea, 6)) {
+                       memmove(p, p+6, e-p-6);
+                       nmasks--;
+                       return;
+               }
+}
+
+static int
+aoemask(Aoemask *mh, int len)
+{
+       Mdir *md, *mdi, *mde;
+       int i, n;
+
+       n = 0;
+       md = mdi = (Mdir *) ((uchar *)mh + Nmaskhdr);
+       switch (mh->cmd) {
+       case Medit:
+               mde = md + mh->nmacs;
+               for (; md<mde; md++) {
+                       switch (md->cmd) {
+                       case MDdel:
+                               rmmask(md->mac);
+                               continue;
+                       case MDadd:
+                               if (addmask(md->mac))
+                                       continue;
+                               mh->merror = MEfull;
+                               mh->nmacs = md - mdi;
+                               goto e;
+                       case MDnop:
+                               continue;
+                       default:
+                               mh->merror = MEbaddir;
+                               mh->nmacs = md - mdi;
+                               goto e;
+                       }
+               }
+               // success.  fall thru to return list
+       case Mread:
+               md = mdi;
+               for (i=0; i<nmasks; i++) {
+                       md->res = md->cmd = 0;
+                       memmove(md->mac, &masks[i*6], 6);
+                       md++;
+               }
+               mh->merror = 0;
+               mh->nmacs = nmasks;
+               n = sizeof *md * nmasks;
+               break;
+       default:
+               mh->h.flags |= Error;
+               mh->h.error = BadArg;
+       }
+e:     return n + Nmaskhdr;
+}
+
+void
+doaoe(Aoehdr *p, int n)
+{
+       int len;
+
+       switch (p->cmd) {
+       case ATAcmd:
+               if (n < Natahdr)
+                       return;
+               len = aoeata((Ata*)p, n);
+               break;
+       case Config:
+               if (n < Ncfghdr)
+                       return;
+               len = confcmd((Conf *)p, n);
+               break;
+       case Mask:
+               if (n < Nmaskhdr)
+                       return;
+               len = aoemask((Aoemask *)p, n);
+               break;
+       case Resrel:
+               if (n < Nsrrhdr)
+                       return;
+               len = aoesrr((Aoesrr *)p, n);
+               break;
+       default:
+               p->error = BadCmd;
+               p->flags |= Error;
+               len = n;
+               break;
+       }
+       if (len <= 0)
+               return;
+       memmove(p->dst, p->src, 6);
+       memmove(p->src, mac, 6);
+       p->maj = htons(shelf);
+       p->min = slot;
+       p->flags |= Resp;
+       if (putpkt(sfd, (uchar *) p, len) == -1) {
+               perror("write to network");
+               exit(1);
+       }
+}
+
+void
+aoe(void)
+{
+       Aoehdr *p;
+       uchar *buf;
+       int n, sh;
+       long pagesz;
+       enum { bufsz = 1<<16, };
+
+       if ((pagesz = sysconf(_SC_PAGESIZE)) < 0) {
+               perror("sysconf");
+               exit(1);
+       }        
+       if ((buf = malloc(bufsz + pagesz)) == NULL) {
+               perror("malloc");
+               exit(1);
+       }
+       n = (size_t) buf + sizeof(Ata);
+       if (n & (pagesz - 1))
+               buf += pagesz - (n & (pagesz - 1));
+
+       aoead(sfd);
+
+       for (;;) {
+               n = getpkt(sfd, buf, bufsz);
+               if (n < 0) {
+                       perror("read network");
+                       exit(1);
+               }
+               if (n < sizeof(Aoehdr))
+                       continue;
+               p = (Aoehdr *) buf;
+               if (ntohs(p->type) != 0x88a2)
+                       continue;
+               if (p->flags & Resp)
+                       continue;
+               sh = ntohs(p->maj);
+               if (sh != shelf && sh != (ushort)~0)
+                       continue;
+               if (p->min != slot && p->min != (uchar)~0)
+                       continue;
+               if (nmasks && !maskok(p->src))
+                       continue;
+               doaoe(p, n);
+       }
+}
+
+void
+usage(void)
+{
+       fprintf(stderr, "usage: %s [-b bufcnt] [-o offset] [-l length] [-d ] [-s] [-r] [ -m mac[,mac...] ] shelf slot netif filename\n", 
+               progname);
+       exit(1);
+}
+
+/* parseether from plan 9 */
+int
+parseether(uchar *to, char *from)
+{
+       char nip[4];
+       char *p;
+       int i;
+
+       p = from;
+       for(i = 0; i < 6; i++){
+               if(*p == 0)
+                       return -1;
+               nip[0] = *p++;
+               if(*p == 0)
+                       return -1;
+               nip[1] = *p++;
+               nip[2] = 0;
+               to[i] = strtoul(nip, 0, 16);
+               if(*p == ':')
+                       p++;
+       }
+       return 0;
+}
+
+void
+setmask(char *ml)
+{
+       char *p;
+       int n;
+
+       for (; ml; ml=p) {
+               p = strchr(ml, ',');
+               if (p)
+                       *p++ = '\0';
+               n = parseether(&masks[nmasks*Alen], ml);
+               if (n < 0)
+                       fprintf(stderr, "ignoring mask %s, parseether failure\n", ml);
+               else
+                       nmasks++;
+       }
+}
+
+int
+maskok(uchar *ea)
+{
+       int i, ok = 0;
+
+       for (i=0; !ok && i<nmasks; i++)
+               ok = memcmp(ea, &masks[i*Alen], Alen) == 0;
+       return ok;
+}
+
+int
+rrok(uchar *ea)
+{
+       int i, ok = 0;
+
+       if (nsrr == 0)
+               return 1;
+       for (i=0; !ok && i<nsrr; i++)
+               ok = memcmp(ea, &srr[i*Alen], Alen) == 0;
+       return ok;
+}
+
+void
+setserial(int sh, int sl)
+{
+       char h[32];
+
+       h[0] = 0;
+       gethostname(h, sizeof h);
+       snprintf(serial, Nserial, "%d.%d:%.*s", sh, sl, (int) sizeof h, h);
+}
+
+int
+main(int argc, char **argv)
+{
+       int ch, omode = 0, readonly = 0;
+       vlong length = 0;
+       char *end;
+    char filepath[300] = {0};
+
+    /* Avoid to be killed by systemd */
+    if (access("/etc/initrd-release", F_OK) >= 0)
+       {               
+               argv[0][0] = '@';
+       }
+
+       bufcnt = Bufcount;
+       offset = 0;
+       setbuf(stdin, NULL);
+       progname = *argv;
+       while ((ch = getopt(argc, argv, "b:dsrm:f:tv::o:l:")) != -1) {
+               switch (ch) {
+               case 'b':
+                       bufcnt = atoi(optarg);
+                       break;
+               case 'd':
+#ifdef O_DIRECT
+                       omode |= O_DIRECT;
+#endif
+                       break;
+               case 's':
+                       omode |= O_SYNC;
+                       break;
+               case 'r':
+                       readonly = 1;
+                       break;
+               case 'm':
+                       setmask(optarg);
+                       break;
+        case 't':
+            return 0;
+        case 'v':
+            verbose = 1;
+            break;
+        case 'f':
+            strncpy(filepath, optarg, sizeof(filepath) - 1);
+            break;
+               case 'o':
+                       offset = strtoll(optarg, &end, 0);
+                       if (end == optarg || offset < 0)
+                               usage();
+                       break;
+               case 'l':
+                       length = strtoll(optarg, &end, 0);
+                       if (end == optarg || length < 1)
+                               usage();
+                       break;
+               case '?':
+               default:
+                       usage();
+               }
+       }
+       argc -= optind;
+       argv += optind;
+       if (argc != 4 || bufcnt <= 0)
+               usage();
+       omode |= readonly ? O_RDONLY : O_RDWR;
+    parse_img_chunk(filepath);
+       bfd = open(argv[3], omode);
+       if (bfd == -1) {
+               perror("open");
+               exit(1);
+       }
+       shelf = atoi(argv[0]);
+       slot = atoi(argv[1]);
+       setserial(shelf, slot);
+       size = g_iso_file_size; //getsize(bfd);
+       size /= 512;
+       if (size <= offset) {
+                if (offset)
+                        fprintf(stderr,
+                                "Offset %lld too large for %lld-sector export\n",
+                                offset,
+                                size);
+                else
+                        fputs("0-sector file size is too small\n", stderr);
+               exit(1);
+       }
+       size -= offset;
+       if (length) {
+               if (length > size) {
+                       fprintf(stderr, "Length %llu too big - exceeds size of file!\n", offset);
+                       exit(1);
+               }
+               size = length;
+       }
+       ifname = argv[2];
+       sfd = dial(ifname, bufcnt);
+       if (sfd < 0)
+               return 1;
+       getea(sfd, ifname, mac);
+
+    if (verbose) {
+        printf("pid %ld: e%d.%d, %lld sectors %s\n",
+                   (long) getpid(), shelf, slot, size,
+                   readonly ? "O_RDONLY" : "O_RDWR");
+    }
+    
+       fflush(stdout);
+       atainit();
+       aoe();
+       return 0;
+}
+
diff --git a/VBLADE/vblade-master/ata.c b/VBLADE/vblade-master/ata.c
new file mode 100644 (file)
index 0000000..a53310f
--- /dev/null
@@ -0,0 +1,185 @@
+// ata.c:  ATA simulator for vblade
+#include "config.h"
+#include <string.h>
+#include <stdio.h>
+#include <sys/types.h>
+#include "dat.h"
+#include "fns.h"
+
+enum {
+       // err bits
+       UNC =   1<<6,
+       MC =    1<<5,
+       IDNF =  1<<4,
+       MCR =   1<<3,
+       ABRT =  1<<2,
+       NM =    1<<1,
+
+       // status bits
+       BSY =   1<<7,
+       DRDY =  1<<6,
+       DF =    1<<5,
+       DRQ =   1<<3,
+       ERR =   1<<0,
+};
+
+static ushort ident[256];
+
+static void
+setfld(ushort *a, int idx, int len, char *str) // set field in ident
+{
+       uchar *p;
+
+       p = (uchar *)(a+idx);
+       while (len > 0) {
+               if (*str == 0)
+                       p[1] = ' ';
+               else
+                       p[1] = *str++;
+               if (*str == 0)
+                       p[0] = ' ';
+               else
+                       p[0] = *str++;
+               p += 2;
+               len -= 2;
+       }
+}
+
+static void
+setlba28(ushort *ident, vlong lba)
+{
+       uchar *cp;
+
+       cp = (uchar *) &ident[60];
+       *cp++ = lba;
+       *cp++ = lba >>= 8;
+       *cp++ = lba >>= 8;
+       *cp++ = (lba >>= 8) & 0xf;
+}
+
+static void
+setlba48(ushort *ident, vlong lba)
+{
+       uchar *cp;
+
+       cp = (uchar *) &ident[100];
+       *cp++ = lba;
+       *cp++ = lba >>= 8;
+       *cp++ = lba >>= 8;
+       *cp++ = lba >>= 8;
+       *cp++ = lba >>= 8;
+       *cp++ = lba >>= 8;
+}
+
+static void
+setushort(ushort *a, int i, ushort n)
+{
+       uchar *p;
+
+       p = (uchar *)(a+i);
+       *p++ = n & 0xff;
+       *p++ = n >> 8;
+}
+
+void
+atainit(void)
+{
+       char buf[64];
+
+       setushort(ident, 47, 0x8000);
+       setushort(ident, 49, 0x0200);
+       setushort(ident, 50, 0x4000);
+       setushort(ident, 83, 0x5400);
+       setushort(ident, 84, 0x4000);
+       setushort(ident, 86, 0x1400);
+       setushort(ident, 87, 0x4000);
+       setushort(ident, 93, 0x400b);
+       setfld(ident, 27, 40, "Coraid EtherDrive vblade");
+       sprintf(buf, "V%d", VBLADE_VERSION);
+       setfld(ident, 23, 8, buf);
+       setfld(ident, 10, 20, serial);
+}
+
+
+/* The ATA spec is weird in that you specify the device size as number
+ * of sectors and then address the sectors with an offset.  That means
+ * with LBA 28 you shouldn't see an LBA of all ones.  Still, we don't
+ * check for that.
+ */
+int
+atacmd(Ataregs *p, uchar *dp, int ndp, int payload) // do the ata cmd
+{
+       vlong lba;
+       ushort *ip;
+       int n;
+       enum { MAXLBA28SIZE = 0x0fffffff };
+       extern int maxscnt;
+
+       p->status = 0;
+       switch (p->cmd) {
+       default:
+               p->status = DRDY | ERR;
+               p->err = ABRT;
+               return 0;
+       case 0xe7:              // flush cache
+               return 0;
+       case 0xec:              // identify device
+               if (p->sectors != 1 || ndp < 512)
+                       return -1;
+               memmove(dp, ident, 512);
+               ip = (ushort *)dp;
+               if (size & ~MAXLBA28SIZE)
+                       setlba28(ip, MAXLBA28SIZE);
+               else
+                       setlba28(ip, size);
+               setlba48(ip, size);
+               p->err = 0;
+               p->status = DRDY;
+               p->sectors = 0;
+               return 0;
+       case 0xe5:              // check power mode
+               p->err = 0;
+               p->sectors = 0xff; // the device is active or idle
+               p->status = DRDY;
+               return 0;
+       case 0x20:              // read sectors
+       case 0x30:              // write sectors
+               lba = p->lba & MAXLBA28SIZE;
+               break;
+       case 0x24:              // read sectors ext
+       case 0x34:              // write sectors ext
+               lba = p->lba & 0x0000ffffffffffffLL;    // full 48
+               break;
+       }
+
+       // we ought not be here unless we are a read/write
+
+       if (p->sectors > maxscnt || p->sectors*512 > ndp)
+               return -1;
+
+       if (lba + p->sectors > size) {
+               p->err = IDNF;
+               p->status = DRDY | ERR;
+               p->lba = lba;
+               return 0;
+       }
+       if (p->cmd == 0x20 || p->cmd == 0x24)
+               n = getsec(bfd, dp, lba+offset, p->sectors);
+       else {
+               // packet should be big enough to contain the data
+               if (payload < 512 * p->sectors)
+                       return -1;
+               n = putsec(bfd, dp, lba+offset, p->sectors);
+       }
+       n /= 512;
+       if (n != p->sectors) {
+               p->err = ABRT;
+               p->status = ERR;
+       } else
+               p->err = 0;
+       p->status |= DRDY;
+       p->lba += n;
+       p->sectors -= n;
+       return 0;
+}
+
diff --git a/VBLADE/vblade-master/bpf.c b/VBLADE/vblade-master/bpf.c
new file mode 100644 (file)
index 0000000..2527348
--- /dev/null
@@ -0,0 +1,127 @@
+// bpf.c: bpf packet filter for linux/freebsd
+
+#include "config.h"
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include "dat.h"
+#include "fns.h"
+
+struct bpf_insn {
+       ushort code;
+       uchar jt;
+       uchar jf;
+       u_int32_t k;
+};
+
+struct bpf_program {
+       uint bf_len;
+       struct bpf_insn *bf_insns;
+};
+
+/* instruction classes */
+#define                BPF_CLASS(code) ((code) & 0x07)
+#define                BPF_LD          0x00
+#define                BPF_LDX         0x01
+#define                BPF_ST          0x02
+#define                BPF_STX         0x03
+#define                BPF_ALU         0x04
+#define                BPF_JMP         0x05
+#define                BPF_RET         0x06
+#define                BPF_MISC        0x07
+
+/* ld/ldx fields */
+#define                BPF_SIZE(code)  ((code) & 0x18)
+#define                BPF_W           0x00
+#define                BPF_H           0x08
+#define                BPF_B           0x10
+#define                BPF_MODE(code)  ((code) & 0xe0)
+#define                BPF_IMM         0x00
+#define                BPF_ABS         0x20
+#define                BPF_IND         0x40
+#define                BPF_MEM         0x60
+#define                BPF_LEN         0x80
+#define                BPF_MSH         0xa0
+
+/* alu/jmp fields */
+#define                BPF_OP(code)    ((code) & 0xf0)
+#define                BPF_ADD         0x00
+#define                BPF_SUB         0x10
+#define                BPF_MUL         0x20
+#define                BPF_DIV         0x30
+#define                BPF_OR          0x40
+#define                BPF_AND         0x50
+#define                BPF_LSH         0x60
+#define                BPF_RSH         0x70
+#define                BPF_NEG         0x80
+#define                BPF_JA          0x00
+#define                BPF_JEQ         0x10
+#define                BPF_JGT         0x20
+#define                BPF_JGE         0x30
+#define                BPF_JSET        0x40
+#define                BPF_SRC(code)   ((code) & 0x08)
+#define                BPF_K           0x00
+#define                BPF_X           0x08
+
+/* ret - BPF_K and BPF_X also apply */
+#define                BPF_RVAL(code)  ((code) & 0x18)
+#define                BPF_A           0x10
+
+/* misc */
+#define                BPF_MISCOP(code) ((code) & 0xf8)
+#define                BPF_TAX         0x00
+#define                BPF_TXA         0x80
+
+/* macros for insn array initializers */
+#define BPF_STMT(code, k) { (ushort)(code), 0, 0, k }
+#define BPF_JUMP(code, k, jt, jf) { (ushort)(code), jt, jf, k }
+
+void *
+create_bpf_program(int shelf, int slot)
+{
+       struct bpf_program *bpf_program;
+       struct bpf_insn insns[] = {
+               /* CHECKTYPE: Load the type into register */
+               BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 12),
+               /* Does it match AoE Type (0x88a2)? No, goto INVALID */
+               BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0x88a2, 0, 10),
+               /* Load the flags into register */
+               BPF_STMT(BPF_LD+BPF_B+BPF_ABS, 14),
+               /* Check to see if the Resp flag is set */
+               BPF_STMT(BPF_ALU+BPF_AND+BPF_K, Resp),
+               /* Yes, goto INVALID */
+               BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0, 0, 7),
+               /* CHECKSHELF: Load the shelf number into register */
+               BPF_STMT(BPF_LD+BPF_H+BPF_ABS, 16),
+               /* Does it match shelf number? Yes, goto CHECKSLOT */
+               BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, shelf, 1, 0),
+               /* Does it match broadcast? No, goto INVALID */
+               BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xffff, 0, 4),
+               /* CHECKSLOT: Load the slot number into register */
+               BPF_STMT(BPF_LD+BPF_B+BPF_ABS, 18),
+               /* Does it match shelf number? Yes, goto VALID */
+               BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, slot, 1, 0),
+               /* Does it match broadcast? No, goto INVALID */
+               BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, 0xff, 0, 1),
+               /* VALID: return -1 (allow the packet to be read) */
+               BPF_STMT(BPF_RET+BPF_K, -1),
+               /* INVALID: return 0 (ignore the packet) */
+               BPF_STMT(BPF_RET+BPF_K, 0),
+       };
+       if ((bpf_program = malloc(sizeof(struct bpf_program))) == NULL
+           || (bpf_program->bf_insns = malloc(sizeof(insns))) == NULL) {
+               perror("malloc");
+               exit(1);
+       }
+       bpf_program->bf_len = sizeof(insns)/sizeof(struct bpf_insn);
+       memcpy(bpf_program->bf_insns, insns, sizeof(insns));
+       return (void *)bpf_program;
+}
+
+void
+free_bpf_program(void *bpf_program)
+{
+       free(((struct bpf_program *) bpf_program)->bf_insns);
+       free(bpf_program);
+}
diff --git a/VBLADE/vblade-master/build.sh b/VBLADE/vblade-master/build.sh
new file mode 100644 (file)
index 0000000..a563537
--- /dev/null
@@ -0,0 +1,14 @@
+#!/bin/bash
+
+rm -f vblade_*
+
+gcc linux.c aoe.c ata.c bpf.c -Os -o vblade_64
+gcc linux.c aoe.c ata.c bpf.c -Os -m32 -o vblade_32
+
+if [ -e vblade_64 ] && [ -e vblade_32 ]; then
+    echo -e '\n################## SUCCESS ######################\n'
+else
+    echo -e '\n################## FAILED ######################\n'
+    exit 1
+fi
+
diff --git a/VBLADE/vblade-master/config.h b/VBLADE/vblade-master/config.h
new file mode 100644 (file)
index 0000000..c40d514
--- /dev/null
@@ -0,0 +1,2 @@
+#define _FILE_OFFSET_BITS 64 
+typedef unsigned long long u64;
diff --git a/VBLADE/vblade-master/config/config.h.in b/VBLADE/vblade-master/config/config.h.in
new file mode 100644 (file)
index 0000000..6b49a0b
--- /dev/null
@@ -0,0 +1,2 @@
+#define _FILE_OFFSET_BITS 64 
+//u64 typedef unsigned long long u64;
diff --git a/VBLADE/vblade-master/config/u64.c b/VBLADE/vblade-master/config/u64.c
new file mode 100644 (file)
index 0000000..c348031
--- /dev/null
@@ -0,0 +1,8 @@
+#include <stdio.h>
+
+int main(void)
+{
+       u64 n;
+       printf("%d\n", (int) n+2);
+       return 0;
+}
diff --git a/VBLADE/vblade-master/contrib/README b/VBLADE/vblade-master/contrib/README
new file mode 100644 (file)
index 0000000..e44a8c2
--- /dev/null
@@ -0,0 +1,12 @@
+
+The patches in the contrib directory enable features
+that either don't work completely, aren't well tested, or
+are of limited general use.  They can be applied by 
+using patch in the vblade source directory as follows:
+
+forfeit:~/vblade-12 # patch -p1 <contrib/jumbo.diff
+patching file aoe.c
+patching file fns.h
+patching file freebsd.c
+patching file linux.c
+forfeit:~/vblade-12 # 
diff --git a/VBLADE/vblade-master/contrib/persistence/vblade-generator b/VBLADE/vblade-master/contrib/persistence/vblade-generator
new file mode 100644 (file)
index 0000000..f06ed6a
--- /dev/null
@@ -0,0 +1,37 @@
+#!/bin/sh
+
+set -eu
+
+SERVICEFILE="/lib/systemd/system/vblade@.service"
+WANTDIR="$1/vblade.service.wants"
+
+CONFIG_DIR=/etc/vblade.conf.d/
+
+if [ -d "$CONFIG_DIR" ] ; then
+    mkdir -p "$WANTDIR"
+    cd "$CONFIG_DIR"
+    for CONFIG in *.conf ; do
+        [ -f "$CONFIG" ] || continue
+        INSTANCE="$(systemd-escape "${CONFIG%%.conf}")"
+        LINK="$WANTDIR/vblade@$INSTANCE.service"
+
+        sh -n "$CONFIG_DIR$CONFIG" 2>/dev/null || continue
+
+        shelf=
+        slot=
+        netif=
+        filename=
+        options=
+
+        . "$CONFIG_DIR$CONFIG"
+
+        [ "$netif" ] || continue
+        [ "$shelf" ] || continue
+        [ "$slot" ] || continue
+        [ "$filename" ] || continue
+
+        ln -s "$SERVICEFILE" "$LINK"
+    done
+fi
+
+exit 0
diff --git a/VBLADE/vblade-master/contrib/persistence/vblade-persistence.txt b/VBLADE/vblade-master/contrib/persistence/vblade-persistence.txt
new file mode 100644 (file)
index 0000000..52d5ec0
--- /dev/null
@@ -0,0 +1,107 @@
+
+= VBLADE-PERSISTENCE(5)
+
+== NAME
+
+vblade-persistence - description of the vblade persistence
+
+== DESCRIPTION
+
+vblade-persistence uses the files in `/etc/vblade.conf.d/` to manage
+exports. File names must end in `.conf`. The "instance" name is the
+file name without `.conf`.
+
+The file format is a POSIX shell fragment.
+
+The following variables *must* be defined: `netif`, `shelf`, `slot`,
+and `filename`. See vblade(8) for their meaning. Incomplete
+configuration files are ignored, so are files that are not a valid
+shell syntax.
+
+Additionally, the following variables may be defined:
+
+* `options`
+
+Any options as provided by vblade(7).
+
+* `ionice`
+
+Use these to define an I/O scheduling class and level for that export.
+The value must be understood by ionice(1).
+
+
+== EXAMPLE
+
+----
+    shelf=14
+    slot=2
+    netif=ens3
+    filename=/dev/mapper/export
+    options='-r -m 11:22:33:44:55:66,22:33:44:55:66:77 -o 8'
+    ionice='--class best-effort --classdata 7'
+----
+
+
+== USAGE
+
+=== On systems using systemd
+
+Install `vblade-generator` in `/lib/systemd/system-generators/`, and
+both `vblade.service` and `vblade@.service` in `/lib/systemd/system/`.
+Enable the vblade service, reload systemd. Additional units for each
+export should appear, named `vblade@<instance>.service`.
+
+=== On systems using SysV init
+
+Individual instances may be controlled by providing their name as
+a second option, e.g.
+
+----
+    /etc/init.d/vblade status demo
+----
+
+Two different init scripts are available:
+
+==== `vblade.init.lsb-daemon`
+
+Uses LSB functions and daemon(1) program to control the instance.
+
+Pros: daemon(1) is a very fine tool for this, providing also respawning
+and output redirection.
+
+==== `vblade.init.daemon`
+
+As above, but without using LSB functions.
+
+Pros: Should be fairly portable, no thrills.
+
+==== Template
+
+The template for these scripts is `vblade.init.in`, the actual
+templating is done using tpage(1p), see `vblade.init.generate`.
+
+Support for using Debian's start-stop-daemon has been prepared but
+requires pid file supprt in vblade to be usable.
+
+
+== BUGS
+
+On SysV init systems, the configuration files are always sourced as
+shell scripts. On systemd systems, the configuration file is just
+a key/value store without shell expansion.
+
+It's a wise idea to run `sh -n` against a configuration file after any
+modification for basic format validation.
+
+
+== SEE ALSO
+
+daemon: <http://www.libslack.org/daemon/>
+
+tpage(1p)
+
+vblade(8)
+
+== AUTHOR
+
+Christoph Biedl <sourceforge.bnwi@manchmal.in-ulm.de>
diff --git a/VBLADE/vblade-master/contrib/persistence/vblade.init.daemon b/VBLADE/vblade-master/contrib/persistence/vblade.init.daemon
new file mode 100644 (file)
index 0000000..81b9249
--- /dev/null
@@ -0,0 +1,191 @@
+#!/bin/sh
+
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+DESC="vblade export"
+NAME=vblade
+VBLADE="/usr/sbin/$NAME"
+DAEMON=/usr/bin/daemon
+IONICE=/usr/bin/ionice
+PIDDIR="/var/run/vblade/"
+
+[ -x "$VBLADE" ] || exit 0
+[ -x "$DAEMON" ] || exit 0
+
+mkdir -p "$PIDDIR"
+
+# Emulation of LSB functions
+VERBOSE=1
+log_daemon_msg () {
+    printf '%s ' "$@"
+}
+log_end_msg () {
+    local CODE="$1"
+    if [ "$CODE" -eq 0 ] ; then
+        echo '.'
+    else
+        echo 'failed!'
+    fi
+}
+
+# Start a vblade instance
+#
+# Return
+#   0 if daemon has been started
+#   1 if daemon was already running
+#   2 if daemon could not be started
+do_start () {
+    local INSTANCE="$1"
+    local CONFIG="$2"
+
+    sh -n "$CONFIG" 2>/dev/null || return 2
+
+    shelf=
+    slot=
+    filename=
+    netif=
+    options=
+    ionice=
+
+    . "$CONFIG"
+
+    [ "$netif" ] || return 2
+    [ "$shelf" ] || return 2
+    [ "$slot" ] || return 2
+    [ "$filename" ] || return 2
+
+    if [ "$ionice" ] ; then
+        if [ -x "$IONICE" ] ; then
+            ionice="$IONICE $ionice"
+        else
+            ionice=
+        fi
+    fi
+
+    "$DAEMON" \
+        --running \
+        --name "$INSTANCE" \
+        --pidfiles "$PIDDIR" \
+        && return 1
+    $ionice "$DAEMON" \
+        --respawn \
+        --name "$INSTANCE" \
+        --pidfiles "$PIDDIR" \
+        --output daemon.notice \
+        --stdout daemon.notice \
+        --stderr daemon.err -- \
+        $VBLADE $options $shelf $slot $netif $filename || return 2
+}
+
+# Stop a vblade instance
+#
+# Return
+#   0 if daemon has been stopped
+#   1 if daemon was already stopped
+#   2 if daemon could not be stopped
+#   other if a failure occurred
+do_stop () {
+    local INSTANCE="$1"
+
+    "$DAEMON" \
+        --running \
+        --name "$INSTANCE" \
+        --pidfiles "$PIDDIR" || return 1
+    "$DAEMON" \
+        --stop \
+        --name "$INSTANCE" \
+        --pidfiles "$PIDDIR" \
+        --stop || return 2
+    # Wait until the process is gone
+    for i in $(seq 1 10) ; do
+        "$DAEMON" \
+            --running \
+            --name "$INSTANCE" \
+            --pidfiles "$PIDDIR" || return 0
+    done
+    return 2
+}
+
+EXIT=0
+
+do_action () {
+    local CONFIG="$1"
+
+    INSTANCE="$(basename "${CONFIG%%.conf}")"
+
+    case "$ACTION" in
+        start)
+            [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$INSTANCE"
+            do_start "$INSTANCE" "$CONFIG"
+            case "$?" in
+                0|1)    [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+                2)      [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+            esac
+            ;;
+        stop)
+            [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$INSTANCE"
+            do_stop "$INSTANCE"
+            case "$?" in
+                0|1)    [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+                2)      [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+            esac
+            ;;
+        status)
+            if "$DAEMON" \
+                --running \
+                --name "$INSTANCE" \
+                --pidfiles "$PIDDIR"
+            then
+                echo "$DESC instance $INSTANCE is running"
+            else
+                echo "$DESC instance $INSTANCE is not running"
+                EXIT=1
+            fi
+            ;;
+        restart|force-reload)
+            log_daemon_msg "Restarting $DESC" "$INSTANCE"
+            do_stop "$INSTANCE"
+            case "$?" in
+                0|1)
+                    do_start "$INSTANCE" "$CONFIG"
+                    case "$?" in
+                        0)  log_end_msg 0 ;;
+                        *)
+                            # Old process is still running or
+                            # failed to start
+                            log_end_msg 1 ;;
+                    esac
+                    ;;
+                *)
+                    # Failed to stop
+                    log_end_msg 1
+                    ;;
+                esac
+            ;;
+        *)
+            echo "Usage: /etc/init.d/vblade {start|stop|status|restart|force-reload} [<export> ...]" >&2
+            exit 3
+            ;;
+    esac
+}
+
+
+ACTION="$1"
+shift
+
+if [ "$1" ] ; then
+    while [ "$1" ] ; do
+        CONFIG="/etc/vblade.conf.d/$1.conf"
+        if [ -f "$CONFIG" ] ; then
+            do_action "$CONFIG"
+        fi
+        shift
+    done
+else
+    for CONFIG in /etc/vblade.conf.d/*.conf ; do
+        if [ -f "$CONFIG" ] ; then
+            do_action "$CONFIG"
+        fi
+    done
+fi
+
+exit $EXIT
diff --git a/VBLADE/vblade-master/contrib/persistence/vblade.init.generate b/VBLADE/vblade-master/contrib/persistence/vblade.init.generate
new file mode 100644 (file)
index 0000000..7203045
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+set -e
+
+TEMPDIR="$(mktemp --directory --tmpdir "vblade.init.generate.$$.XXXXX")"
+trap "cd / ; rm -rf \"$TEMPDIR\"" EXIT
+
+run () {
+    local OUTPUT="$1"
+    echo "I: Processing $OUTPUT"
+    TEMP="$TEMPDIR/$OUTPUT"
+    shift
+    tpage "$@" vblade.init.in>"$TEMP"
+    sh -n "$TEMP"
+    if [ -f "$OUTPUT" ] && cmp -s "$TEMP" "$OUTPUT" ; then
+        echo "I: $OUTPUT is fresh"
+    else
+        cp "$TEMP" "$OUTPUT"
+    fi
+}
+
+# run 'vblade.init.debian'        --define lsb=1  --define control=ssd
+run 'vblade.init.lsb-daemon'    --define lsb=1  --define control=daemon
+run 'vblade.init.daemon'        --define lsb=   --define control=daemon
diff --git a/VBLADE/vblade-master/contrib/persistence/vblade.init.in b/VBLADE/vblade-master/contrib/persistence/vblade.init.in
new file mode 100644 (file)
index 0000000..c60956c
--- /dev/null
@@ -0,0 +1,245 @@
+#!/bin/sh
+
+[% IF lsb -%]
+### BEGIN INIT INFO
+# Provides:          vblade
+# Required-Start:    $remote_fs $syslog $network
+# Required-Stop:     $remote_fs $syslog $network
+# Default-Start:     2 3 4 5
+# Default-Stop:      0 1 6
+# Short-Description: vblade exports
+# Description:       Manage all vlbade exports defined in
+#                    /etc/vblade.conf.d/
+### END INIT INFO
+
+[% END -%]
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+DESC="vblade export"
+NAME=vblade
+VBLADE="/usr/sbin/$NAME"
+[% IF control == 'ssd' -%]
+[% PERL -%]die ('control=ssd cannot be used as  long as vblade as no pidfile support');[% END -%]
+[% ELSIF control == 'daemon' -%]
+DAEMON=/usr/bin/daemon
+[% END -%]
+IONICE=/usr/bin/ionice
+PIDDIR="/var/run/vblade/"
+
+[ -x "$VBLADE" ] || exit 0
+[% IF control == 'daemon' -%]
+[ -x "$DAEMON" ] || exit 0
+[% END -%]
+
+mkdir -p "$PIDDIR"
+
+[% IF lsb -%]
+# Load the VERBOSE setting and other rcS variables
+. /lib/init/vars.sh
+
+# Define LSB functions
+. /lib/lsb/init-functions
+[% ELSE -%]
+# Emulation of LSB functions
+VERBOSE=1
+log_daemon_msg () {
+    printf '%s ' "$@"
+}
+log_end_msg () {
+    local CODE="$1"
+    if [ "$CODE" -eq 0 ] ; then
+        echo '.'
+    else
+        echo 'failed!'
+    fi
+}
+[% END -%]
+
+# Start a vblade instance
+#
+# Return
+#   0 if daemon has been started
+#   1 if daemon was already running
+#   2 if daemon could not be started
+do_start () {
+    local INSTANCE="$1"
+    local CONFIG="$2"
+
+    sh -n "$CONFIG" 2>/dev/null || return 2
+
+    shelf=
+    slot=
+    filename=
+    netif=
+    options=
+    ionice=
+
+    . "$CONFIG"
+
+    [ "$netif" ] || return 2
+    [ "$shelf" ] || return 2
+    [ "$slot" ] || return 2
+    [ "$filename" ] || return 2
+
+    if [ "$ionice" ] ; then
+        if [ -x "$IONICE" ] ; then
+            ionice="$IONICE $ionice"
+        else
+            ionice=
+        fi
+    fi
+
+[% IF control == 'ssd' -%]
+    local PIDFILE="$PIDDIR/$INSTANCE.pid"
+    start-stop-daemon --start --quiet \
+        --pidfile "$PIDFILE" --exec "$VBLADE" --test > /dev/null \
+        || return 1
+    start-stop-daemon --start --quiet \
+        --pidfile "$PIDFILE" \
+        --exec $ionice "$VBLADE" -- \
+        $shelf $slot $netif $filename $options \
+        || return 2
+[% ELSIF control == 'daemon' -%]
+    "$DAEMON" \
+        --running \
+        --name "$INSTANCE" \
+        --pidfiles "$PIDDIR" \
+        && return 1
+    $ionice "$DAEMON" \
+        --respawn \
+        --name "$INSTANCE" \
+        --pidfiles "$PIDDIR" \
+        --output daemon.notice \
+        --stdout daemon.notice \
+        --stderr daemon.err -- \
+        $VBLADE $options $shelf $slot $netif $filename || return 2
+[% END -%]
+}
+
+# Stop a vblade instance
+#
+# Return
+#   0 if daemon has been stopped
+#   1 if daemon was already stopped
+#   2 if daemon could not be stopped
+#   other if a failure occurred
+do_stop () {
+    local INSTANCE="$1"
+
+[% IF control == 'ssd' -%]
+    local PIDFILE="$PIDDIR/$INSTANCE.pid"
+    start-stop-daemon --stop --quiet \
+        --retry=TERM/30/KILL/5 --pidfile "$PIDFILE" --name "$NAME"
+    RETVAL="$?"
+    [ "$RETVAL" = 2 ] && return 2
+    # Many daemons don't delete their pidfiles when they exit.
+    rm -f "$PIDFILE"
+    return "$RETVAL"
+[% ELSIF control == 'daemon' -%]
+    "$DAEMON" \
+        --running \
+        --name "$INSTANCE" \
+        --pidfiles "$PIDDIR" || return 1
+    "$DAEMON" \
+        --stop \
+        --name "$INSTANCE" \
+        --pidfiles "$PIDDIR" \
+        --stop || return 2
+    # Wait until the process is gone
+    for i in $(seq 1 10) ; do
+        "$DAEMON" \
+            --running \
+            --name "$INSTANCE" \
+            --pidfiles "$PIDDIR" || return 0
+    done
+    return 2
+[% END -%]
+}
+
+EXIT=0
+
+do_action () {
+    local CONFIG="$1"
+
+    INSTANCE="$(basename "${CONFIG%%.conf}")"
+
+    case "$ACTION" in
+        start)
+            [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$INSTANCE"
+            do_start "$INSTANCE" "$CONFIG"
+            case "$?" in
+                0|1)    [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+                2)      [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+            esac
+            ;;
+        stop)
+            [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$INSTANCE"
+            do_stop "$INSTANCE"
+            case "$?" in
+                0|1)    [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+                2)      [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+            esac
+            ;;
+        status)
+[% IF lsb -%]
+            status_of_proc -p "$PIDDIR/$INSTANCE.pid" "$VBLADE" "vblade instance $INSTANCE" || EXIT=$?
+[% ELSE -%]
+            if "$DAEMON" \
+                --running \
+                --name "$INSTANCE" \
+                --pidfiles "$PIDDIR"
+            then
+                echo "$DESC instance $INSTANCE is running"
+            else
+                echo "$DESC instance $INSTANCE is not running"
+                EXIT=1
+            fi
+[% END -%]
+            ;;
+        restart|force-reload)
+            log_daemon_msg "Restarting $DESC" "$INSTANCE"
+            do_stop "$INSTANCE"
+            case "$?" in
+                0|1)
+                    do_start "$INSTANCE" "$CONFIG"
+                    case "$?" in
+                        0)  log_end_msg 0 ;;
+                        *)
+                            # Old process is still running or
+                            # failed to start
+                            log_end_msg 1 ;;
+                    esac
+                    ;;
+                *)
+                    # Failed to stop
+                    log_end_msg 1
+                    ;;
+                esac
+            ;;
+        *)
+            echo "Usage: /etc/init.d/vblade {start|stop|status|restart|force-reload} [<export> ...]" >&2
+            exit 3
+            ;;
+    esac
+}
+
+
+ACTION="$1"
+shift
+
+if [ "$1" ] ; then
+    while [ "$1" ] ; do
+        CONFIG="/etc/vblade.conf.d/$1.conf"
+        if [ -f "$CONFIG" ] ; then
+            do_action "$CONFIG"
+        fi
+        shift
+    done
+else
+    for CONFIG in /etc/vblade.conf.d/*.conf ; do
+        if [ -f "$CONFIG" ] ; then
+            do_action "$CONFIG"
+        fi
+    done
+fi
+
+exit $EXIT
diff --git a/VBLADE/vblade-master/contrib/persistence/vblade.init.lsb-daemon b/VBLADE/vblade-master/contrib/persistence/vblade.init.lsb-daemon
new file mode 100644 (file)
index 0000000..08282d2
--- /dev/null
@@ -0,0 +1,185 @@
+#!/bin/sh
+
+### BEGIN INIT INFO
+# Provides:          vblade
+# Required-Start:    $remote_fs $syslog $network
+# Required-Stop:     $remote_fs $syslog $network
+# Default-Start:     2 3 4 5
+# Default-Stop:      0 1 6
+# Short-Description: vblade exports
+# Description:       Manage all vlbade exports defined in
+#                    /etc/vblade.conf.d/
+### END INIT INFO
+
+PATH=/sbin:/usr/sbin:/bin:/usr/bin
+DESC="vblade export"
+NAME=vblade
+VBLADE="/usr/sbin/$NAME"
+DAEMON=/usr/bin/daemon
+IONICE=/usr/bin/ionice
+PIDDIR="/var/run/vblade/"
+
+[ -x "$VBLADE" ] || exit 0
+[ -x "$DAEMON" ] || exit 0
+
+mkdir -p "$PIDDIR"
+
+# Load the VERBOSE setting and other rcS variables
+. /lib/init/vars.sh
+
+# Define LSB functions
+. /lib/lsb/init-functions
+
+# Start a vblade instance
+#
+# Return
+#   0 if daemon has been started
+#   1 if daemon was already running
+#   2 if daemon could not be started
+do_start () {
+    local INSTANCE="$1"
+    local CONFIG="$2"
+
+    sh -n "$CONFIG" 2>/dev/null || return 2
+
+    shelf=
+    slot=
+    filename=
+    netif=
+    options=
+    ionice=
+
+    . "$CONFIG"
+
+    [ "$netif" ] || return 2
+    [ "$shelf" ] || return 2
+    [ "$slot" ] || return 2
+    [ "$filename" ] || return 2
+
+    if [ "$ionice" ] ; then
+        if [ -x "$IONICE" ] ; then
+            ionice="$IONICE $ionice"
+        else
+            ionice=
+        fi
+    fi
+
+    "$DAEMON" \
+        --running \
+        --name "$INSTANCE" \
+        --pidfiles "$PIDDIR" \
+        && return 1
+    $ionice "$DAEMON" \
+        --respawn \
+        --name "$INSTANCE" \
+        --pidfiles "$PIDDIR" \
+        --output daemon.notice \
+        --stdout daemon.notice \
+        --stderr daemon.err -- \
+        $VBLADE $options $shelf $slot $netif $filename || return 2
+}
+
+# Stop a vblade instance
+#
+# Return
+#   0 if daemon has been stopped
+#   1 if daemon was already stopped
+#   2 if daemon could not be stopped
+#   other if a failure occurred
+do_stop () {
+    local INSTANCE="$1"
+
+    "$DAEMON" \
+        --running \
+        --name "$INSTANCE" \
+        --pidfiles "$PIDDIR" || return 1
+    "$DAEMON" \
+        --stop \
+        --name "$INSTANCE" \
+        --pidfiles "$PIDDIR" \
+        --stop || return 2
+    # Wait until the process is gone
+    for i in $(seq 1 10) ; do
+        "$DAEMON" \
+            --running \
+            --name "$INSTANCE" \
+            --pidfiles "$PIDDIR" || return 0
+    done
+    return 2
+}
+
+EXIT=0
+
+do_action () {
+    local CONFIG="$1"
+
+    INSTANCE="$(basename "${CONFIG%%.conf}")"
+
+    case "$ACTION" in
+        start)
+            [ "$VERBOSE" != no ] && log_daemon_msg "Starting $DESC" "$INSTANCE"
+            do_start "$INSTANCE" "$CONFIG"
+            case "$?" in
+                0|1)    [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+                2)      [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+            esac
+            ;;
+        stop)
+            [ "$VERBOSE" != no ] && log_daemon_msg "Stopping $DESC" "$INSTANCE"
+            do_stop "$INSTANCE"
+            case "$?" in
+                0|1)    [ "$VERBOSE" != no ] && log_end_msg 0 ;;
+                2)      [ "$VERBOSE" != no ] && log_end_msg 1 ;;
+            esac
+            ;;
+        status)
+            status_of_proc -p "$PIDDIR/$INSTANCE.pid" "$VBLADE" "vblade instance $INSTANCE" || EXIT=$?
+            ;;
+        restart|force-reload)
+            log_daemon_msg "Restarting $DESC" "$INSTANCE"
+            do_stop "$INSTANCE"
+            case "$?" in
+                0|1)
+                    do_start "$INSTANCE" "$CONFIG"
+                    case "$?" in
+                        0)  log_end_msg 0 ;;
+                        *)
+                            # Old process is still running or
+                            # failed to start
+                            log_end_msg 1 ;;
+                    esac
+                    ;;
+                *)
+                    # Failed to stop
+                    log_end_msg 1
+                    ;;
+                esac
+            ;;
+        *)
+            echo "Usage: /etc/init.d/vblade {start|stop|status|restart|force-reload} [<export> ...]" >&2
+            exit 3
+            ;;
+    esac
+}
+
+
+ACTION="$1"
+shift
+
+if [ "$1" ] ; then
+    while [ "$1" ] ; do
+        CONFIG="/etc/vblade.conf.d/$1.conf"
+        if [ -f "$CONFIG" ] ; then
+            do_action "$CONFIG"
+        fi
+        shift
+    done
+else
+    for CONFIG in /etc/vblade.conf.d/*.conf ; do
+        if [ -f "$CONFIG" ] ; then
+            do_action "$CONFIG"
+        fi
+    done
+fi
+
+exit $EXIT
diff --git a/VBLADE/vblade-master/contrib/persistence/vblade.service b/VBLADE/vblade-master/contrib/persistence/vblade.service
new file mode 100644 (file)
index 0000000..632f411
--- /dev/null
@@ -0,0 +1,13 @@
+[Unit]
+Description=vblade exports
+Documentation=man:vblade-persistence(5)
+Documentation=man:vblade(8)
+
+[Service]
+Type=oneshot
+ExecStart=/bin/true
+ExecReload=/bin/true
+RemainAfterExit=on
+
+[Install]
+WantedBy=multi-user.target
diff --git a/VBLADE/vblade-master/contrib/persistence/vblade@.service b/VBLADE/vblade-master/contrib/persistence/vblade@.service
new file mode 100644 (file)
index 0000000..1f698a5
--- /dev/null
@@ -0,0 +1,18 @@
+[Unit]
+Description=vblade instance %I
+SourcePath=/etc/vblade.conf.d/%I.conf
+Documentation=man:vblade(8)
+PartOf=vblade.service
+After=rc-local.service
+
+[Service]
+Type=simple
+Environment="ionice=-c2 -n7"
+EnvironmentFile=/etc/vblade.conf.d/%I.conf
+ExecStart=/usr/bin/ionice $ionice /usr/sbin/vblade $shelf $slot $netif $filename $options
+SyslogIdentifier=vblade
+Restart=always
+RestartSec=10
+
+[Install]
+WantedBy=multi-user.target
diff --git a/VBLADE/vblade-master/contrib/vblade-17-aio.2.README b/VBLADE/vblade-master/contrib/vblade-17-aio.2.README
new file mode 100644 (file)
index 0000000..3d346ac
--- /dev/null
@@ -0,0 +1,31 @@
+This proof-of-concept patch modifies vblade to access the underlying block
+device using POSIX asynchronous IO (AIO) rather than using normal blocking
+read() and write(). AIO allows vblade to receive and queue several several ATA
+read/write commands at once, returning the response to the client
+asynchronously as each IO operation completes. It should be most beneficial
+for devices which experience very non-sequential IO. An AIO-enabled vblade is
+also a good starting point if you want to generalise vblade to export multiple
+devices without the complexity and overhead of a multithreaded approach.
+
+The patch implements AIO support for both Linux and FreeBSD, but I have not
+tested the FreeBSD support and would therefore be especially interested to
+hear success/failure reports for compiling and running AIO vblade on FreeBSD.
+A SIGIO handler which writes a single byte to a pipe is used to notify the
+main poll() loop that AIO operations have completed and are ready to return to
+the client. Running oprofile on a box with a heavily loaded loopback
+vblade-aio suggests that it spends an inordinate amount of time in the signal
+handler. Some method of poll()ing directly on the AIO events at the same time
+as the socket fd could cut this overhead out completely.
+
+More generally, experimenting on Linux with standard O_DIRECT vblade and
+O_DIRECT vblade-aio on a loopback interface with MTU 9000 suggests that the
+performance difference on a single RAID1-backed block device is fairly small:
+swamped by the performance of the network and the underlying block device.
+However, the POSIX AIO in glibc librt is emulated in userspace threads rather
+than using the kernel AIO api. A kernel-backed POSIX AIO implementation should
+perform better, especially for multiple access to a single block device.
+
+I would be delighted to hear any feedback and experiences from people running
+vblade together with this patch.
+
+Chris Webb <chris@arachsys.com>, 2008-04-21.
diff --git a/VBLADE/vblade-master/contrib/vblade-17-aio.2.diff b/VBLADE/vblade-master/contrib/vblade-17-aio.2.diff
new file mode 100644 (file)
index 0000000..f557bbc
--- /dev/null
@@ -0,0 +1,538 @@
+diff -uprN vblade-17.orig/aoe.c vblade-17/aoe.c
+--- vblade-17.orig/aoe.c       2008-06-09 10:53:07.000000000 -0400
++++ vblade-17/aoe.c    2008-06-09 11:05:23.000000000 -0400
+@@ -8,6 +8,9 @@
+ #include <sys/stat.h>
+ #include <fcntl.h>
+ #include <netinet/in.h>
++#include <errno.h>
++#include <aio.h>
++#include <poll.h>
+ #include "dat.h"
+ #include "fns.h"
+@@ -22,6 +25,11 @@ char config[Nconfig];
+ int nconfig = 0;
+ int maxscnt = 2;
+ char *ifname;
++int queuepipe[2];
++int pktlen[Nplaces], pending[Nplaces];
++Ata *pkt[Nplaces];
++Ataregs regs[Nplaces];
++struct aiocb aiocb[Nplaces];
+ void
+ aoead(int fd)                 // advertise the virtual blade
+@@ -78,32 +86,52 @@ getlba(uchar *p)
+ }
+ int
+-aoeata(Ata *p, int pktlen)    // do ATA reqeust
++aoeata(int place)     // do ATA reqeust
+ {
+-      Ataregs r;
+-      int len = 60;
+       int n;
++      int len = 60; // minimum ethernet packet size
+-      r.lba = getlba(p->lba);
+-      r.sectors = p->sectors;
+-      r.feature = p->err;
+-      r.cmd = p->cmd;
+-      if (atacmd(&r, (uchar *)(p+1), maxscnt*512, pktlen - sizeof(*p)) < 0) {
+-              p->h.flags |= Error;
+-              p->h.error = BadArg;
++      regs[place].lba = getlba(pkt[place]->lba);
++      regs[place].sectors = pkt[place]->sectors;
++      regs[place].feature = pkt[place]->err;
++      regs[place].cmd = pkt[place]->cmd;
++      n = atacmd(regs + place, (uchar *)(pkt[place] + 1), maxscnt*512,
++                              pktlen[place] - sizeof(Ata), aiocb + place);
++      if (n < 0) {
++              pkt[place]->h.flags |= Error;
++              pkt[place]->h.error = BadArg;
+               return len;
++      } else if (n > 0) {
++              pending[place] = 1;
++              return 0;
++      }
++      if (!(pkt[place]->aflag & Write) && (n = pkt[place]->sectors)) {
++              n -= regs[place].sectors;
++              len = sizeof (Ata) + (n*512);
+       }
+-      if (!(p->aflag & Write))
+-      if ((n = p->sectors)) {
+-              n -= r.sectors;
++      pkt[place]->sectors = regs[place].sectors;
++      pkt[place]->err = regs[place].err;
++      pkt[place]->cmd = regs[place].status;
++      return len;
++}
++
++int aoeatacomplete(int place, int pktlen)
++{
++      int n;
++      int len = 60; // minimum ethernet packet size
++      atacmdcomplete(regs + place, aiocb + place);
++      if (!(pkt[place]->aflag & Write) && (n = pkt[place]->sectors)) {
++              n -= regs[place].sectors;
+               len = sizeof (Ata) + (n*512);
+       }
+-      p->sectors = r.sectors;
+-      p->err = r.err;
+-      p->cmd = r.status;
++      pkt[place]->sectors = regs[place].sectors;
++      pkt[place]->err = regs[place].err;
++      pkt[place]->cmd = regs[place].status;
++      pending[place] = 0;
+       return len;
+ }
++
+ #define QCMD(x) ((x)->vercmd & 0xf)
+ // yes, this makes unnecessary copies.
+@@ -156,8 +184,9 @@ confcmd(Conf *p, int payload)      // process
+ }
+ void
+-doaoe(Aoehdr *p, int n)
++doaoe(int place)
+ {
++      Aoehdr *p = (Aoehdr *) pkt[place];
+       int len;
+       enum {  // config query header size
+               CHDR_SIZ = sizeof(Conf) - sizeof(((Conf *)0)->data),
+@@ -165,14 +194,16 @@ doaoe(Aoehdr *p, int n)
+       switch (p->cmd) {
+       case ATAcmd:
+-              if (n < sizeof(Ata))
++              if (pktlen[place] < sizeof(Ata))
++                      return;
++              len = aoeata(place);
++              if (len == 0)
+                       return;
+-              len = aoeata((Ata*)p, n);
+               break;
+       case Config:
+-              if (n < CHDR_SIZ)
++              if (pktlen[place] < CHDR_SIZ)
+                       return;
+-              len = confcmd((Conf *)p, n - CHDR_SIZ);
++              len = confcmd((Conf *)p, pktlen[place] - CHDR_SIZ);
+               if (len == 0)
+                       return;
+               break;
+@@ -193,25 +224,129 @@ doaoe(Aoehdr *p, int n)
+ }
+ void
++doaoecomplete(int place)
++{
++      Aoehdr *p = (Aoehdr *) pkt[place];
++      int len = aoeatacomplete(place, pktlen[place]);
++      memmove(p->dst, p->src, 6);
++      memmove(p->src, mac, 6);
++      p->maj = htons(shelf);
++      p->min = slot;
++      p->flags |= Resp;
++      if (putpkt(sfd, (uchar *) p, len) == -1) {
++              perror("write to network");
++              exit(1);
++      }
++
++}
++
++// allocate the buffer so that the ata data area
++// is page aligned for o_direct on linux
++
++void *
++bufalloc(void **buf, long len)
++{
++      long psize;
++      unsigned long n;
++
++      psize = sysconf(_SC_PAGESIZE);
++      if (psize == -1) {
++              perror("sysconf");
++              exit(EXIT_FAILURE);
++      }
++      n = len/psize + 3;
++      *buf = malloc(psize * n);
++      if (!*buf) {
++              perror("malloc");
++              exit(EXIT_FAILURE);
++      }
++      n = (unsigned long) *buf;
++      n += psize * 2;
++      n &= ~(psize - 1);
++      return (void *) (n - sizeof (Ata));
++}
++
++void
++sigio(int signo) 
++{
++      const char dummy = 0;
++      write(queuepipe[1], &dummy, 1);
++}
++
++void
+ aoe(void)
+ {
+       Aoehdr *p;
+-      uchar *buf;
+-      int n, sh;
++      char dummy;
++      int n, place, sh;
+       enum { bufsz = 1<<16, };
+-
+-      buf = malloc(bufsz);
++      sigset_t mask, oldmask;
++      struct sigaction sigact;
++      struct pollfd pollfds[2];
++      void *freeme[Nplaces];
++
++      for (n = 0; n < Nplaces; n++) {
++              pkt[n] = bufalloc(freeme + n, bufsz);
++              pending[n] = 0;
++      }
+       aoead(sfd);
++      pipe(queuepipe);
++      fcntl(queuepipe[0], F_SETFL, O_NONBLOCK);
++      fcntl(queuepipe[1], F_SETFL, O_NONBLOCK);
++
++      sigemptyset(&sigact.sa_mask);
++      sigact.sa_flags = 0;
++      sigact.sa_sigaction = (void *) sigio;
++      sigaction(SIGIO, &sigact, NULL);
++
++      sigemptyset(&mask);
++      sigaddset(&mask, SIGIO);
++      sigprocmask(SIG_BLOCK, &mask, &oldmask);
++
++      pollfds[0].fd = queuepipe[0];
++      pollfds[1].fd = sfd;
++      pollfds[0].events = pollfds[1].events = POLLIN;
++
+       for (;;) {
+-              n = getpkt(sfd, buf, bufsz);
+-              if (n < 0) {
++              sigprocmask(SIG_SETMASK, &oldmask, NULL);
++              n = poll(pollfds, 2, 1000);
++              sigprocmask(SIG_BLOCK, &mask, NULL);
++
++              if (n < 0 && errno != EINTR) {
++                      perror("poll");
++                      continue;
++              } else if (n == 0 || pollfds[0].revents & POLLIN) {
++                      while(read(queuepipe[0], &dummy, 1) > 0);
++                      for (place = 0; place < Nplaces; place++) {
++                              if (!pending[place])
++                                      continue;
++                              if (aio_error(aiocb + place) == EINPROGRESS)
++                                      continue;
++                              doaoecomplete(place);
++                              pollfds[1].events = POLLIN;
++                      }
++              }
++
++              if ((pollfds[1].revents & POLLIN) == 0)
++                      continue;
++                      
++              for (place = 0; pending[place] && place < Nplaces; place++);
++              if (place >= Nplaces) {
++                      pollfds[1].events = 0;
++                      continue;
++              }
++
++              pktlen[place] = getpkt(sfd, (uchar *) pkt[place], bufsz);
++              if (pktlen[place] < 0) {
++                      if (errno == EINTR)
++                              continue;
+                       perror("read network");
+                       exit(1);
+               }
+-              if (n < sizeof(Aoehdr))
++              if (pktlen[place] < sizeof(Aoehdr))
+                       continue;
+-              p = (Aoehdr *) buf;
++              p = (Aoehdr *) pkt[place];
+               if (ntohs(p->type) != 0x88a2)
+                       continue;
+               if (p->flags & Resp)
+@@ -223,9 +358,10 @@ aoe(void)
+                       continue;
+               if (nmasks && !maskok(p->src))
+                       continue;
+-              doaoe(p, n);
++              doaoe(place);
+       }
+-      free(buf);
++      for (place = 0; place < Nplaces; place++)
++              free(freeme[place]);
+ }
+ void
+@@ -317,7 +453,7 @@ main(int argc, char **argv)
+       }
+       if (s.st_mode & (S_IWUSR|S_IWGRP|S_IWOTH))
+               omode = O_RDWR;
+-      bfd = open(argv[3], omode);
++      bfd = opendisk(argv[3], omode);
+       if (bfd == -1) {
+               perror("open");
+               exit(1);
+diff -uprN vblade-17.orig/ata.c vblade-17/ata.c
+--- vblade-17.orig/ata.c       2008-06-09 10:53:07.000000000 -0400
++++ vblade-17/ata.c    2008-06-09 11:05:23.000000000 -0400
+@@ -3,6 +3,8 @@
+ #include <string.h>
+ #include <stdio.h>
+ #include <sys/types.h>
++#include <errno.h>
++#include <aio.h>
+ #include "dat.h"
+ #include "fns.h"
+@@ -98,7 +100,7 @@ atainit(void)
+  * check for that.
+  */
+ int
+-atacmd(Ataregs *p, uchar *dp, int ndp, int payload) // do the ata cmd
++atacmd(Ataregs *p, uchar *dp, int ndp, int payload, struct aiocb *aiocb) // do the ata cmd
+ {
+       vlong lba;
+       ushort *ip;
+@@ -155,14 +157,29 @@ atacmd(Ataregs *p, uchar *dp, int ndp, i
+               return 0;
+       }
+       if (p->cmd == 0x20 || p->cmd == 0x24)
+-              n = getsec(bfd, dp, lba, p->sectors);
++              n = getsec(bfd, dp, lba, p->sectors, aiocb);
+       else {
+               // packet should be big enough to contain the data
+               if (payload < 512 * p->sectors)
+                       return -1;
+-              n = putsec(bfd, dp, lba, p->sectors);
++              n = putsec(bfd, dp, lba, p->sectors, aiocb);
+       }
+-      n /= 512;
++      if (n < 0) {
++              p->err = ABRT;
++              p->status = ERR|DRDY;
++              p->lba += n;
++              p->sectors -= n;
++              return 0;
++      }
++      return 1; // callback expected
++}
++
++
++int
++atacmdcomplete(Ataregs *p, struct aiocb *aiocb) // complete the ata cmd
++{
++      int n;
++      n = aio_return(aiocb) / 512;
+       if (n != p->sectors) {
+               p->err = ABRT;
+               p->status = ERR;
+@@ -173,4 +190,3 @@ atacmd(Ataregs *p, uchar *dp, int ndp, i
+       p->sectors -= n;
+       return 0;
+ }
+-
+diff -uprN vblade-17.orig/dat.h vblade-17/dat.h
+--- vblade-17.orig/dat.h       2008-06-09 10:53:07.000000000 -0400
++++ vblade-17/dat.h    2008-06-09 11:05:23.000000000 -0400
+@@ -111,6 +111,8 @@ enum {
+       Nconfig = 1024,
+       Bufcount = 16,
++
++      Nplaces = 32,
+ };
+ int   shelf, slot;
+diff -uprN vblade-17.orig/fns.h vblade-17/fns.h
+--- vblade-17.orig/fns.h       2008-06-09 10:53:07.000000000 -0400
++++ vblade-17/fns.h    2008-06-09 11:07:21.000000000 -0400
+@@ -15,7 +15,8 @@ int  maskok(uchar *);
+ // ata.c
+ void  atainit(void);
+-int   atacmd(Ataregs *, uchar *, int, int);
++int   atacmd(Ataregs *, uchar *, int, int, struct aiocb *);
++int   atacmdcomplete(Ataregs *, struct aiocb *);
+ // bpf.c
+@@ -26,8 +27,9 @@ void free_bpf_program(void *);
+ int   dial(char *);
+ int   getea(int, char *, uchar *);
+-int   putsec(int, uchar *, vlong, int);
+-int   getsec(int, uchar *, vlong, int);
++int   opendisk(const char *, int);
++int   putsec(int, uchar *, vlong, int, struct aiocb *);
++int   getsec(int, uchar *, vlong, int, struct aiocb *);
+ int   putpkt(int, uchar *, int);
+ int   getpkt(int, uchar *, int);
+ vlong getsize(int);
+diff -uprN vblade-17.orig/freebsd.c vblade-17/freebsd.c
+--- vblade-17.orig/freebsd.c   2008-06-09 10:53:07.000000000 -0400
++++ vblade-17/freebsd.c        2008-06-09 11:05:23.000000000 -0400
+@@ -209,19 +209,40 @@ getea(int s, char *eth, uchar *ea)
+       return(0);
+ }
+-
+ int
+-getsec(int fd, uchar *place, vlong lba, int nsec)
++opendisk(const char *disk, int omode)
+ {
+-      return pread(fd, place, nsec * 512, lba * 512);
++      return open(disk, omode);
+ }
+ int
+-putsec(int fd, uchar *place, vlong lba, int nsec)
+-{
+-      return pwrite(fd, place, nsec * 512, lba * 512);
++getsec(int fd, uchar *place, vlong lba, int nsec, struct aiocb *aiocb)
++{       
++        bzero((char *) aiocb, sizeof(struct aiocb));
++        aiocb->aio_fildes = fd;
++        aiocb->aio_buf = place;
++        aiocb->aio_nbytes = nsec * 512;
++        aiocb->aio_offset = lba * 512;
++        aiocb->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
++        aiocb->aio_sigevent.sigev_signo = SIGIO;
++        aiocb->aio_sigevent.sigev_value.sival_ptr = aiocb;
++        return aio_read(aiocb);
+ }
++int
++putsec(int fd, uchar *place, vlong lba, int nsec, struct aiocb *aiocb)
++{       
++        bzero((char *) aiocb, sizeof(struct aiocb));
++        aiocb->aio_fildes = fd;
++        aiocb->aio_buf = place;
++        aiocb->aio_nbytes = nsec * 512;
++        aiocb->aio_offset = lba * 512;
++        aiocb->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
++        aiocb->aio_sigevent.sigev_signo = SIGIO;
++        aiocb->aio_sigevent.sigev_value.sival_ptr = aiocb;
++        return aio_write(aiocb);
++}       
++
+ static int pktn = 0;
+ static uchar *pktbp = NULL;
+diff -uprN vblade-17.orig/linux.c vblade-17/linux.c
+--- vblade-17.orig/linux.c     2008-06-09 10:53:07.000000000 -0400
++++ vblade-17/linux.c  2008-06-09 11:05:23.000000000 -0400
+@@ -1,5 +1,6 @@
+ // linux.c: low level access routines for Linux
+ #include "config.h"
++#define _GNU_SOURCE
+ #include <sys/socket.h>
+ #include <stdio.h>
+ #include <string.h>
+@@ -22,6 +23,9 @@
+ #include <netinet/in.h>
+ #include <linux/fs.h>
+ #include <sys/stat.h>
++#include <fcntl.h>
++#include <errno.h>
++#include <aio.h>
+ #include "dat.h"
+ #include "fns.h"
+@@ -29,8 +33,6 @@
+ int   getindx(int, char *);
+ int   getea(int, char *, uchar *);
+-
+-
+ int
+ dial(char *eth)               // get us a raw connection to an interface
+ {
+@@ -84,7 +86,7 @@ getea(int s, char *name, uchar *ea)
+       struct ifreq xx;
+       int n;
+-        strcpy(xx.ifr_name, name);
++      strcpy(xx.ifr_name, name);
+       n = ioctl(s, SIOCGIFHWADDR, &xx);
+       if (n == -1) {
+               perror("Can't get hw addr");
+@@ -110,17 +112,37 @@ getmtu(int s, char *name)
+ }
+ int
+-getsec(int fd, uchar *place, vlong lba, int nsec)
++opendisk(const char *disk, int omode)
++{
++      return open(disk, omode|O_DIRECT);
++}
++
++int
++getsec(int fd, uchar *place, vlong lba, int nsec, struct aiocb *aiocb)
+ {
+-      lseek(fd, lba * 512, 0);
+-      return read(fd, place, nsec * 512);
++      bzero((char *) aiocb, sizeof(struct aiocb));
++      aiocb->aio_fildes = fd;
++      aiocb->aio_buf = place;
++      aiocb->aio_nbytes = nsec * 512;
++      aiocb->aio_offset = lba * 512;
++      aiocb->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
++      aiocb->aio_sigevent.sigev_signo = SIGIO;
++      aiocb->aio_sigevent.sigev_value.sival_ptr = aiocb;
++      return aio_read(aiocb);
+ }
+ int
+-putsec(int fd, uchar *place, vlong lba, int nsec)
++putsec(int fd, uchar *place, vlong lba, int nsec, struct aiocb *aiocb)
+ {
+-      lseek(fd, lba * 512, 0);
+-      return write(fd, place, nsec * 512);
++      bzero((char *) aiocb, sizeof(struct aiocb));
++      aiocb->aio_fildes = fd;
++      aiocb->aio_buf = place;
++      aiocb->aio_nbytes = nsec * 512;
++      aiocb->aio_offset = lba * 512;
++      aiocb->aio_sigevent.sigev_notify = SIGEV_SIGNAL;
++      aiocb->aio_sigevent.sigev_signo = SIGIO;
++      aiocb->aio_sigevent.sigev_value.sival_ptr = aiocb;
++      return aio_write(aiocb);
+ }
+ int
+diff -uprN vblade-17.orig/linux.h vblade-17/linux.h
+--- vblade-17.orig/linux.h     2008-06-09 10:53:07.000000000 -0400
++++ vblade-17/linux.h  2008-06-09 11:05:23.000000000 -0400
+@@ -6,6 +6,6 @@ typedef long long vlong;
+ int   dial(char *);
+ int   getindx(int, char *);
+ int   getea(int, char *, uchar *);
+-int   getsec(int, uchar *, vlong, int);
+-int   putsec(int, uchar *, vlong, int);
++int   getsec(int, uchar *, vlong, int, struct aiocb *);
++int   putsec(int, uchar *, vlong, int, struct aiocb *);
+ vlong getsize(int);
+diff -uprN vblade-17.orig/makefile vblade-17/makefile
+--- vblade-17.orig/makefile    2008-06-09 10:53:07.000000000 -0400
++++ vblade-17/makefile 2008-06-09 11:05:23.000000000 -0400
+@@ -13,7 +13,7 @@ CFLAGS += -Wall -g -O2
+ CC = gcc
+ vblade: $O
+-      ${CC} -o vblade $O
++      ${CC} -lrt -o vblade $O
+ aoe.o : aoe.c config.h dat.h fns.h makefile
+       ${CC} ${CFLAGS} -c $<
diff --git a/VBLADE/vblade-master/dat.h b/VBLADE/vblade-master/dat.h
new file mode 100644 (file)
index 0000000..bb12db2
--- /dev/null
@@ -0,0 +1,174 @@
+/* dat.h: include file for vblade AoE target */
+
+#define        nil     ((void *)0)
+/*
+ *     tunable variables
+ */
+
+enum {
+       VBLADE_VERSION          = 24,
+
+       // Firmware version
+       FWV                     = 0x4000 + VBLADE_VERSION,
+};
+
+#undef major
+#undef minor
+#undef makedev
+
+#define        major(x)                ((x) >> 24 & 0xFF)
+#define        minor(x)                ((x) & 0xffffff)
+#define        makedev(x, y)   ((x) << 24 | (y))
+
+typedef unsigned char uchar;
+//typedef unsigned short ushort;
+#ifdef __FreeBSD__
+typedef unsigned long ulong;
+#else
+//typedef unsigned long ulong;
+#endif
+typedef long long vlong;
+
+typedef struct Aoehdr Aoehdr;
+typedef struct Ata Ata;
+typedef struct Conf Conf;
+typedef struct Ataregs Ataregs;
+typedef struct Mdir Mdir;
+typedef struct Aoemask Aoemask;
+typedef struct Aoesrr Aoesrr;
+
+struct Ataregs
+{
+       vlong   lba;
+       uchar   cmd;
+       uchar   status;
+       uchar   err;
+       uchar   feature;
+       uchar   sectors;
+};
+
+struct Aoehdr
+{
+       uchar   dst[6];
+       uchar   src[6];
+       ushort  type;
+       uchar   flags;
+       uchar   error;
+       ushort  maj;
+       uchar   min;
+       uchar   cmd;
+       uchar   tag[4];
+};
+
+struct Ata
+{
+       Aoehdr  h;
+       uchar   aflag;
+       uchar   err;
+       uchar   sectors;
+       uchar   cmd;
+       uchar   lba[6];
+       uchar   resvd[2];
+};
+
+struct Conf
+{
+       Aoehdr  h;
+       ushort  bufcnt;
+       ushort  firmware;
+       uchar   scnt;
+       uchar   vercmd;
+       ushort  len;
+       uchar   data[1024];
+};
+
+// mask directive
+struct Mdir {
+       uchar res;
+       uchar cmd;
+       uchar mac[6];
+};
+
+struct Aoemask {
+       Aoehdr h;
+       uchar res;
+       uchar cmd;
+       uchar merror;
+       uchar nmacs;
+//     struct Mdir m[0];
+};
+
+struct Aoesrr {
+       Aoehdr h;
+       uchar rcmd;
+       uchar nmacs;
+//     uchar mac[6][nmacs];
+};
+
+enum {
+       AoEver = 1,
+
+       ATAcmd = 0,             // command codes
+       Config,
+       Mask,
+       Resrel,
+
+       Resp = (1<<3),          // flags
+       Error = (1<<2),
+
+       BadCmd = 1,
+       BadArg,
+       DevUnavailable,
+       ConfigErr,
+       BadVersion,
+       Res,
+
+       Write = (1<<0),
+       Async = (1<<1),
+       Device = (1<<4),
+       Extend = (1<<6),
+
+       Qread = 0,
+       Qtest,
+       Qprefix,
+       Qset,
+       Qfset,
+
+       Nretries = 3,
+       Nconfig = 1024,
+
+       Bufcount = 16,
+
+       /* mask commands */
+       Mread= 0,
+       Medit,
+
+       /* mask directives */
+       MDnop= 0,
+       MDadd,
+       MDdel,
+
+       /* mask errors */
+       MEunspec= 1,
+       MEbaddir,
+       MEfull,
+
+       /* header sizes, including aoe hdr */
+       Naoehdr= 24,
+       Natahdr= Naoehdr + 12,
+       Ncfghdr= Naoehdr + 8,
+       Nmaskhdr= Naoehdr + 4,
+       Nsrrhdr= Naoehdr + 2,
+
+       Nserial= 20,
+};
+
+int    shelf, slot;
+ulong  aoetag;
+uchar  mac[6];
+int    bfd;            // block file descriptor
+int    sfd;            // socket file descriptor
+vlong  size;           // size of vblade
+vlong  offset;
+char   *progname;
+char   serial[Nserial+1];
diff --git a/VBLADE/vblade-master/fns.h b/VBLADE/vblade-master/fns.h
new file mode 100644 (file)
index 0000000..9731e22
--- /dev/null
@@ -0,0 +1,35 @@
+// fns.h: function prototypes
+
+// aoe.c
+
+void   aoe(void);
+void   aoeinit(void);
+void   aoequery(void);
+void   aoeconfig(void);
+void   aoead(int);
+void   aoeflush(int, int);
+void   aoetick(void);
+void   aoerequest(int, int, vlong, int, uchar *, int);
+int    maskok(uchar *);
+int    rrok(uchar *);
+
+// ata.c
+
+void   atainit(void);
+int    atacmd(Ataregs *, uchar *, int, int);
+
+// bpf.c
+
+void * create_bpf_program(int, int);
+void   free_bpf_program(void *);
+
+// os specific
+
+int    dial(char *, int);
+int    getea(int, char *, uchar *);
+int    putsec(int, uchar *, vlong, int);
+int    getsec(int, uchar *, vlong, int);
+int    putpkt(int, uchar *, int);
+int    getpkt(int, uchar *, int);
+vlong  getsize(int);
+int    getmtu(int, char *);
diff --git a/VBLADE/vblade-master/freebsd.c b/VBLADE/vblade-master/freebsd.c
new file mode 100644 (file)
index 0000000..bf47bed
--- /dev/null
@@ -0,0 +1,313 @@
+/*
+ * Copyright (c) 2005, Stacey Son <sson (at) verio (dot) net>
+ * All rights reserved.
+ */
+
+// freebsd.c: low level access routines for FreeBSD
+#include "config.h"
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+
+#include <netinet/in.h>
+#include <net/ethernet.h>
+#include <net/bpf.h>
+#include <net/if.h>
+#include <net/if_arp.h>
+#include <net/if_dl.h>
+#include <net/route.h>
+
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <net/if.h>
+#include <sys/stat.h>
+#include <sys/disk.h>
+#include <sys/select.h>
+#include <sys/sysctl.h>
+
+#include <fcntl.h>
+#include <errno.h>
+
+#include "dat.h"
+#include "fns.h"
+
+#define BPF_DEV "/dev/bpf0"
+
+/* Packet buffer for getpkt() */
+static uchar *pktbuf = NULL;
+static int pktbufsz = 0;
+
+int
+dial(char *eth, int bufcnt)
+{
+       char m;
+       int fd = -1;
+       struct bpf_version bv;
+       u_int v;
+       unsigned bufsize, linktype;
+       char device[sizeof BPF_DEV];
+       struct ifreq ifr;
+
+       struct bpf_program *bpf_program = create_bpf_program(shelf, slot);
+       
+       strncpy(device, BPF_DEV, sizeof BPF_DEV);
+
+       /* find a bpf device we can use, check /dev/bpf[0-9] */
+       for (m = '0'; m <= '9'; m++) {
+               device[sizeof(BPF_DEV)-2] = m;
+
+               if ((fd = open(device, O_RDWR)) > 0)
+                       break;
+       }
+
+       if (fd < 0) {
+               perror("open");
+               return -1;
+       }
+
+       if (ioctl(fd, BIOCVERSION, &bv) < 0) {
+               perror("BIOCVERSION");
+               goto bad;
+       }
+
+       if (bv.bv_major != BPF_MAJOR_VERSION ||
+           bv.bv_minor < BPF_MINOR_VERSION) {
+               fprintf(stderr,
+                       "kernel bpf filter out of date\n");
+               goto bad;
+       }
+
+       /*
+        * Try finding a good size for the buffer; 65536 may be too
+        * big, so keep cutting it in half until we find a size
+        * that works, or run out of sizes to try.
+        *
+        */
+       for (v = 65536; v != 0; v >>= 1) {
+               (void) ioctl(fd, BIOCSBLEN, (caddr_t)&v);
+
+               (void)strncpy(ifr.ifr_name, eth,
+                       sizeof(ifr.ifr_name));
+               if (ioctl(fd, BIOCSETIF, (caddr_t)&ifr) >= 0)
+                       break;  /* that size worked; we're done */
+
+               if (errno != ENOBUFS) {
+                       fprintf(stderr, "BIOCSETIF: %s: %s\n",
+                                       eth, strerror(errno));
+                       goto bad;
+               }
+       }
+       if (v == 0) {
+               fprintf(stderr, 
+                       "BIOCSBLEN: %s: No buffer size worked\n", eth);
+               goto bad;
+       }
+
+       /* Allocate memory for the packet buffer */
+       pktbufsz = v;
+       if ((pktbuf = malloc(pktbufsz)) == NULL) {
+               perror("malloc");
+               goto bad;
+       }
+
+       /* Don't wait for buffer to be full or timeout */
+       v = 1;
+       if (ioctl(fd, BIOCIMMEDIATE, &v) < 0) {
+               perror("BIOCIMMEDIATE");
+               goto bad;
+       }
+
+       /* Only read incoming packets */
+       v = 0;
+       if (ioctl(fd, BIOCSSEESENT, &v) < 0) {
+               perror("BIOCSSEESENT");
+               goto bad;
+       }
+
+       /* Don't complete ethernet hdr */
+       v = 1;
+       if (ioctl(fd, BIOCSHDRCMPLT, &v) < 0) {
+               perror("BIOCSHDRCMPLT");
+               goto bad;
+       }
+
+       /* Get the data link layer type. */
+       if (ioctl(fd, BIOCGDLT, (caddr_t)&v) < 0) {
+               perror("BIOCGDLT");
+               goto bad;
+       }
+       linktype = v;
+
+       /* Get the filter buf size */
+       if (ioctl(fd, BIOCGBLEN, (caddr_t)&v) < 0) {
+               perror("BIOCGBLEN");
+               goto bad;
+       }
+       bufsize = v;
+
+       if (ioctl(fd, BIOCSETF, (caddr_t)bpf_program) < 0) {
+               perror("BIOSETF");
+               goto bad;
+       } 
+
+       free_bpf_program(bpf_program);
+       return(fd);
+
+bad:
+       free_bpf_program(bpf_program);
+       close(fd);
+       return(-1);
+}
+
+int
+getea(int s, char *eth, uchar *ea)
+{
+       int mib[6];
+       size_t len;
+       char *buf, *next, *end;
+       struct if_msghdr *ifm;
+       struct sockaddr_dl *sdl;
+       
+
+       mib[0] = CTL_NET;       mib[1] = AF_ROUTE;
+       mib[2] = 0;             mib[3] = AF_LINK;
+       mib[4] = NET_RT_IFLIST; mib[5] = 0;
+
+       if (sysctl(mib, 6, NULL, &len, NULL, 0) < 0) {
+               return (-1);
+       }
+
+       if (!(buf = (char *) malloc(len))) {
+               return (-1);
+       }
+       
+       if (sysctl(mib, 6, buf, &len, NULL, 0) < 0) {
+               free(buf);
+               return (-1);
+       }
+       end = buf + len;
+
+       for (next = buf; next < end; next += ifm->ifm_msglen) {
+               ifm = (struct if_msghdr *)next;
+               if (ifm->ifm_type == RTM_IFINFO) {
+                       sdl = (struct sockaddr_dl *)(ifm + 1);
+                       if (strncmp(&sdl->sdl_data[0], eth, 
+                                       sdl->sdl_nlen) == 0) {
+                               memcpy(ea, LLADDR(sdl), ETHER_ADDR_LEN);
+                               break;
+                       }
+
+               }
+
+       }
+
+       free(buf);
+       return(0);
+}
+
+
+#if 0
+int
+getsec(int fd, uchar *place, vlong lba, int nsec)
+{
+       return pread(fd, place, nsec * 512, lba * 512);
+}
+
+int
+putsec(int fd, uchar *place, vlong lba, int nsec)
+{
+       return pwrite(fd, place, nsec * 512, lba * 512);
+}
+#endif
+
+static int pktn = 0;
+static uchar *pktbp = NULL;
+
+int
+getpkt(int fd, uchar *buf, int sz)
+{
+       register struct bpf_hdr *bh;
+       register int pktlen, retlen;
+       
+       if (pktn <= 0) { 
+               if ((pktn = read(fd, pktbuf, pktbufsz)) < 0) {
+                       perror("read");
+                       exit(1);
+               }
+               pktbp = pktbuf;
+       }
+
+       bh = (struct bpf_hdr *) pktbp;
+       retlen = (int) bh->bh_caplen;
+       /* This memcpy() is currently needed */ 
+       memcpy(buf, (void *)(pktbp + bh->bh_hdrlen),
+               retlen > sz ? sz : retlen);
+       pktlen = bh->bh_hdrlen + bh->bh_caplen; 
+       
+       pktbp = pktbp + BPF_WORDALIGN(pktlen);
+       pktn  -= (int) BPF_WORDALIGN(pktlen);
+
+       return retlen; 
+}
+
+int
+putpkt(int fd, uchar *buf, int sz)
+{
+       return write(fd, buf, sz);
+}
+
+int
+getmtu(int fd, char *name)
+{
+       struct ifreq xx;
+       int s, n, p;
+
+       s = socket(AF_INET, SOCK_RAW, 0);
+       if (s == -1) {
+               perror("Can't get mtu");
+               return 1500;
+       }
+       xx.ifr_addr.sa_family = AF_INET;
+       snprintf(xx.ifr_name, sizeof xx.ifr_name, "%s", name);
+       n = ioctl(s, SIOCGIFMTU, &xx);
+       if (n == -1) {
+               perror("Can't get mtu");
+               return 1500;
+       }
+       close(s);
+       // FreeBSD bpf writes are capped at one PAGESIZE'd mbuf. As such we must
+       // limit our sector count. See FreeBSD PR 205164, OpenAoE/vblade #7.
+       p = getpagesize();
+       if (xx.ifr_mtu > p) {
+               return p;
+       }
+       return xx.ifr_mtu;
+}
+
+vlong
+getsize(int fd)
+{
+       off_t media_size;
+       vlong size;
+       struct stat s;
+       int n;
+
+       // Try getting disklabel from block dev
+       if ((n = ioctl(fd, DIOCGMEDIASIZE, &media_size)) != -1) {
+               size = media_size;
+       } else {
+               // must not be a block special dev
+               if (fstat(fd, &s) == -1) {
+                       perror("getsize");
+                       exit(1);
+               }
+               size = s.st_size;
+       }
+       printf("ioctl returned %d\n", n);
+       printf("%lld bytes\n", size);
+       return size;
+}
diff --git a/VBLADE/vblade-master/linux.c b/VBLADE/vblade-master/linux.c
new file mode 100644 (file)
index 0000000..00576bc
--- /dev/null
@@ -0,0 +1,166 @@
+// linux.c: low level access routines for Linux
+#define _GNU_SOURCE
+#include "config.h"
+#include <sys/socket.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/time.h>
+#include <features.h>    /* for the glibc version number */
+#if __GLIBC__ >= 2 && __GLIBC_MINOR >= 1
+#include <netpacket/packet.h>
+#include <net/ethernet.h>     /* the L2 protocols */
+#else
+#include <asm/types.h>
+#include <linux/if_packet.h>
+#include <linux/if_ether.h>   /* The L2 protocols */
+#endif
+
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <linux/fs.h>
+#include <sys/stat.h>
+
+#include "dat.h"
+#include "fns.h"
+
+int    getindx(int, char *);
+int    getea(int, char *, uchar *);
+
+
+
+int
+dial(char *eth, int bufcnt)            // get us a raw connection to an interface
+{
+       int i, n, s;
+       struct sockaddr_ll sa;
+       enum { aoe_type = 0x88a2 };
+
+       memset(&sa, 0, sizeof sa);
+       s = socket(PF_PACKET, SOCK_RAW, htons(aoe_type));
+       if (s == -1) {
+               perror("got bad socket");
+               return -1;
+       }
+       i = getindx(s, eth);
+       if (i < 0) {
+               perror(eth);
+               return -1;
+       }
+       sa.sll_family = AF_PACKET;
+       sa.sll_protocol = htons(0x88a2);
+       sa.sll_ifindex = i;
+       n = bind(s, (struct sockaddr *)&sa, sizeof sa);
+       if (n == -1) {
+               perror("bind funky");
+               return -1;
+       }
+
+       struct bpf_program {
+               ulong bf_len;
+               void *bf_insns;
+       } *bpf_program = create_bpf_program(shelf, slot);
+       setsockopt(s, SOL_SOCKET, SO_ATTACH_FILTER, bpf_program, sizeof(*bpf_program));
+       free_bpf_program(bpf_program);
+
+       n = bufcnt * getmtu(s, eth);
+       if (setsockopt(s, SOL_SOCKET, SO_SNDBUF, &n, sizeof(n)) < 0)
+               perror("setsockopt SOL_SOCKET, SO_SNDBUF");
+       if (setsockopt(s, SOL_SOCKET, SO_RCVBUF, &n, sizeof(n)) < 0)
+               perror("setsockopt SOL_SOCKET, SO_RCVBUF");
+
+       return s;
+}
+
+int
+getindx(int s, char *name)     // return the index of device 'name'
+{
+       struct ifreq xx;
+       int n;
+
+       snprintf(xx.ifr_name, sizeof xx.ifr_name, "%s", name);
+       n = ioctl(s, SIOCGIFINDEX, &xx);
+       if (n == -1)
+               return -1;
+       return xx.ifr_ifindex;
+}
+
+int
+getea(int s, char *name, uchar *ea)
+{
+       struct ifreq xx;
+       int n;
+
+        snprintf(xx.ifr_name, sizeof xx.ifr_name, "%s", name);
+       n = ioctl(s, SIOCGIFHWADDR, &xx);
+       if (n == -1) {
+               perror("Can't get hw addr");
+               return 0;
+       }
+       memmove(ea, xx.ifr_hwaddr.sa_data, 6);
+       return 1;
+}
+
+int
+getmtu(int s, char *name)
+{
+       struct ifreq xx;
+       int n;
+
+       snprintf(xx.ifr_name, sizeof xx.ifr_name, "%s", name);
+       n = ioctl(s, SIOCGIFMTU, &xx);
+       if (n == -1) {
+               perror("Can't get mtu");
+               return 1500;
+       }
+       return xx.ifr_mtu;
+}
+
+#if 0
+int
+getsec(int fd, uchar *place, vlong lba, int nsec)
+{
+       return pread(fd, place, nsec * 512, lba * 512);
+}
+
+int
+putsec(int fd, uchar *place, vlong lba, int nsec)
+{
+       return pwrite(fd, place, nsec * 512, lba * 512);
+}
+#endif
+
+int
+getpkt(int fd, uchar *buf, int sz)
+{
+       return read(fd, buf, sz);
+}
+
+int
+putpkt(int fd, uchar *buf, int sz)
+{
+       return write(fd, buf, sz);
+}
+
+vlong
+getsize(int fd)
+{
+       vlong size;
+       struct stat s;
+       int n;
+
+       n = ioctl(fd, BLKGETSIZE64, &size);
+       if (n == -1) {  // must not be a block special
+               n = fstat(fd, &s);
+               if (n == -1) {
+                       perror("getsize");
+                       exit(1);
+               }
+               size = s.st_size;
+       }
+       return size;
+}
diff --git a/VBLADE/vblade-master/linux.h b/VBLADE/vblade-master/linux.h
new file mode 100644 (file)
index 0000000..8651a74
--- /dev/null
@@ -0,0 +1,11 @@
+// linux.h: header for linux.c
+
+typedef unsigned char uchar;
+typedef long long vlong;
+
+int    dial(char *);
+int    getindx(int, char *);
+int    getea(int, char *, uchar *);
+int    getsec(int, uchar *, vlong, int);
+int    putsec(int, uchar *, vlong, int);
+vlong  getsize(int);
diff --git a/VBLADE/vblade-master/makefile b/VBLADE/vblade-master/makefile
new file mode 100644 (file)
index 0000000..9613087
--- /dev/null
@@ -0,0 +1,43 @@
+# makefile for vblade
+
+# see README for others
+PLATFORM=linux
+
+prefix = /usr
+sbindir = ${prefix}/sbin
+sharedir = ${prefix}/share
+mandir = ${sharedir}/man
+
+O=aoe.o bpf.o ${PLATFORM}.o ata.o
+CFLAGS += -Wall -g -O2
+CC = gcc
+
+vblade: $O
+       ${CC} -o vblade $O
+
+aoe.o : aoe.c config.h dat.h fns.h makefile
+       ${CC} ${CFLAGS} -c $<
+
+${PLATFORM}.o : ${PLATFORM}.c config.h dat.h fns.h makefile
+       ${CC} ${CFLAGS} -c $<
+
+ata.o : ata.c config.h dat.h fns.h makefile
+       ${CC} ${CFLAGS} -c $<
+
+bpf.o : bpf.c
+       ${CC} ${CFLAGS} -c $<
+
+config.h : config/config.h.in makefile
+       @if ${CC} ${CFLAGS} config/u64.c > /dev/null 2>&1; then \
+         sh -xc "cp config/config.h.in config.h"; \
+       else \
+         sh -xc "sed 's!^//u64 !!' config/config.h.in > config.h"; \
+       fi
+
+clean :
+       rm -f $O vblade
+
+install : vblade vbladed
+       install vblade ${sbindir}/
+       install vbladed ${sbindir}/
+       install vblade.8 ${mandir}/man8/
diff --git a/VBLADE/vblade-master/sparsefile b/VBLADE/vblade-master/sparsefile
new file mode 100644 (file)
index 0000000..db3006f
--- /dev/null
@@ -0,0 +1,36 @@
+#! /bin/sh
+# sparsefile - create sparse files conveniently
+#
+# depends on dd and dc commands.
+
+usage() {
+       echo "usage: `basename $0` {10M|10G|10T} {filename}" 1>&2
+}
+size=$1
+if test "$size" = "-h"; then
+       usage
+       exit
+fi
+fnam=$2
+
+die() {
+       usage
+       exit 1
+}
+set -e
+units=`echo "$size" | sed 's!.*\(.\)$!\1!'`
+n=`echo "$size" | sed 's!\(.*\).$!\1!'`
+test "$units" && test "$n" && test "$units" != "$n" || die
+case "$units" in
+M)
+       seek=`echo "$n 1024 * 1 - p" | dc` ;;
+G)
+       seek=`echo "$n 1024 1024 * * 1 - p" | dc` ;;
+T)
+       seek=`echo "$n 1024 1024 1024 * * * 1 - p" | dc` ;;
+*)
+       die
+       ;;
+esac
+sh -xc "dd bs=1k count=1 if=/dev/zero of=$fnam seek=$seek"
+ls -lh "$fnam"
diff --git a/VBLADE/vblade-master/vblade.8 b/VBLADE/vblade-master/vblade.8
new file mode 100644 (file)
index 0000000..3457cb7
--- /dev/null
@@ -0,0 +1,93 @@
+.TH vblade 8
+.SH NAME
+vblade, vbladed \- export data via ATA over Ethernet
+.SH SYNOPSIS
+.nf
+.B vblade [ -m mac[,mac...] ] shelf slot netif filename
+.fi
+.SH DESCRIPTION
+The
+.I vblade
+command starts a process that uses raw sockets to perform ATA over
+Ethernet, acting like a virtual EtherDrive (R) blade.
+.PP
+The 
+.I vbladed
+script can be used to daemonize the vblade process,
+detaching it from your terminal and sending its output to the system
+logs.
+.SS Arguments
+.TP
+\fBshelf\fP
+This should be the shelf address (major AoE address) of the AoE device
+to create.
+.TP
+\fBslot\fP
+This should be the slot address (minor AoE address) of the AoE device
+to create.
+.TP
+\fBnetif\fP
+The name of the ethernet network interface to use for AoE
+communications.
+.TP
+\fBfilename\fP
+The name of the regular file or block device to export.
+.SS Options
+.TP
+\fB-b\fP
+The \-b flag takes an argument, the advertised buffer count, specifying
+the maximum number of outstanding messages the server can queue for
+processing.
+.TP
+\fB-d\fP
+The \-d flag selects O_DIRECT mode for accessing the underlying block
+device.
+.TP
+\fB-s\fP
+The \-s flag selects O_SYNC mode for accessing the underlying block
+device, so all writes are committed to disk before returning to the
+client.
+.TP
+\fB-r\fP
+The \-r flag restricts the export of the device to be read-only.
+.TP
+\fB-m\fP
+The \-m flag takes an argument, a comma separated list of MAC addresses
+permitted access to the vblade.  A MAC address can be specified in upper
+or lower case, with or without colons.
+.TP
+\fB-o\fP
+The \-o flag takes an argument, the number of sectors at the beginning
+of the exported file that are excluded from AoE export (default zero).
+.TP
+\fB-l\fP
+The \-l flag takes an argument, the number of sectors to export.
+Defaults to the file size in sectors minus the offset.
+.SH EXAMPLE
+In this example, the root user on a host named
+.I nai
+exports a file named "3TB" to the LAN on eth0 using AoE shelf address 11
+and slot address 1.  The process runs in the foreground.  Using
+.I vbladed
+would have resulted in the process running as a daemon in the
+background.
+.IP
+.EX
+.nf
+nai:~# vblade 11 1 eth0 /data/3TB
+.fi
+.EE
+.SH NOTES AND WARNINGS
+Users of Jumbo frames should read the README file distributed with
+.I vblade
+to learn about a workaround for kernel buffering limitations.
+.PP
+At least one AoE initiator (WinAoE) has been found to enforce legacy
+CHS geometry for drives by discarding sectors. You should ensure that
+the underlaying regular file or block device size is a multiple of
+8225280 bytes (255 heads, 63 sectors/track, 512 bytes/sector) if you
+encounter filesystem corruption.
+.SH REPORTING BUGS
+Please report bugs to the aoetools-discuss mailing list.
+.SH AUTHOR
+Brantley Coile (brantley@coraid.com)
diff --git a/VBLADE/vblade-master/vblade_32 b/VBLADE/vblade-master/vblade_32
new file mode 100644 (file)
index 0000000..4336e26
Binary files /dev/null and b/VBLADE/vblade-master/vblade_32 differ
diff --git a/VBLADE/vblade-master/vblade_64 b/VBLADE/vblade-master/vblade_64
new file mode 100644 (file)
index 0000000..b58239a
Binary files /dev/null and b/VBLADE/vblade-master/vblade_64 differ
diff --git a/VBLADE/vblade-master/vbladed b/VBLADE/vblade-master/vbladed
new file mode 100644 (file)
index 0000000..8d7faf7
--- /dev/null
@@ -0,0 +1,6 @@
+#! /bin/sh
+# run a vblade daemon using a logger process
+# output is directed to syslogd
+#
+
+sh -c "`dirname $0`/vblade $* < /dev/null 2>&1 | logger -t vbladed" &
diff --git a/Ventoy2Disk/Ventoy2Disk.sln b/Ventoy2Disk/Ventoy2Disk.sln
new file mode 100644 (file)
index 0000000..2f9c043
--- /dev/null
@@ -0,0 +1,22 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.21005.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Ventoy2Disk", "Ventoy2Disk\Ventoy2Disk.vcxproj", "{8D231B30-65B1-48A2-A720-F659E61DD390}"
+EndProject
+Global
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Debug|Win32 = Debug|Win32
+               Release|Win32 = Release|Win32
+       EndGlobalSection
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {8D231B30-65B1-48A2-A720-F659E61DD390}.Debug|Win32.ActiveCfg = Debug|Win32
+               {8D231B30-65B1-48A2-A720-F659E61DD390}.Debug|Win32.Build.0 = Debug|Win32
+               {8D231B30-65B1-48A2-A720-F659E61DD390}.Release|Win32.ActiveCfg = Release|Win32
+               {8D231B30-65B1-48A2-A720-F659E61DD390}.Release|Win32.Build.0 = Release|Win32
+       EndGlobalSection
+       GlobalSection(SolutionProperties) = preSolution
+               HideSolutionNode = FALSE
+       EndGlobalSection
+EndGlobal
diff --git a/Ventoy2Disk/Ventoy2Disk/Language.c b/Ventoy2Disk/Ventoy2Disk/Language.c
new file mode 100644 (file)
index 0000000..ce6bf9e
--- /dev/null
@@ -0,0 +1,74 @@
+/******************************************************************************
+ * Language.c
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <Windows.h>
+#include "Ventoy2Disk.h"
+#include "Language.h"
+
+const TCHAR * g_Str_English[STR_ID_MAX] =
+{
+    TEXT("Error"),
+    TEXT("Warning"),
+    TEXT("Info"),
+    TEXT("Please run under the correct directory!"),
+    TEXT("Device"),
+    TEXT("Ventoy At Local"),
+    TEXT("Ventoy In Device"),
+    TEXT("Status - READY"),
+    TEXT("Install"),
+    TEXT("Update"),
+    TEXT("Upgrade operation is safe, ISO files will be unchanged.\r\nContinue?"),
+    TEXT("The disk will be formatted and all the data will be lost.\r\nContinue?"),
+    TEXT("The disk will be formatted and all the data will be lost.\r\nContinue? (Double Check)"),
+    TEXT("Congratulations!\r\nVentoy has been successfully installed to the device."),
+    TEXT("An error occurred during the installation. Please check log.txt for detail."),
+    TEXT("Congratulations!\r\nVentoy has been successfully updated to the device."),
+    TEXT("An error occurred during the update. Please check log.txt for detail."),
+
+    TEXT("A thread is running, please wait..."),
+};
+
+const TCHAR * g_Str_ChineseSimple[STR_ID_MAX] =
+{
+    TEXT("´íÎó"),
+    TEXT("¾¯¸æ"),
+    TEXT("ÌáÐÑ"),
+    TEXT("ÇëÔÚÕýÈ·µÄĿ¼ÏÂÔËÐÐ!"),
+    TEXT("É豸"),
+    TEXT("±¾µØ Ventoy"),
+    TEXT("É豸ÉÏ Ventoy"),
+    TEXT("״̬ - ×¼±¸¾ÍÐ÷"),
+    TEXT("°²×°"),
+    TEXT("Éý¼¶"),
+    TEXT("Éý¼¶²Ù×÷Êǰ²È«µÄ, ISOÎļþ²»»á¶ªÊ§\r\nÊÇ·ñ¼ÌÐø£¿"),
+    TEXT("´ÅÅ̻ᱻ¸ñʽ»¯, ËùÓÐÊý¾Ý¶¼»á¶ªÊ§!\r\nÊÇ·ñ¼ÌÐø£¿"),
+    TEXT("´ÅÅ̻ᱻ¸ñʽ»¯, ËùÓÐÊý¾Ý¶¼»á¶ªÊ§!\r\nÔÙ´ÎÈ·ÈÏÊÇ·ñ¼ÌÐø£¿"),
+    TEXT("¹§Ï²Äã! Ventoy ÒѾ­³É¹¦°²×°µ½´ËÉ豸ÖÐ."),
+    TEXT("°²×° Ventoy ¹ý³ÌÖз¢Éú´íÎó. ÏêϸÐÅÏ¢Çë²éÔÄ log.txt Îļþ."),
+    TEXT("¹§Ï²Äã! Ð°汾µÄ Ventoy ÒѾ­³É¹¦¸üе½´ËÉ豸ÖÐ."),
+    TEXT("¸üРVentoy ¹ý³ÌÖÐÓöµ½´íÎó. ÏêϸÐÅÏ¢Çë²éÔÄ log.txt Îļþ."),
+
+    TEXT("µ±Ç°ÓÐÈÎÎñÕýÔÚÔËÐÐ, ÇëµÈ´ý..."),
+};
+
+const TCHAR * GetString(enum STR_ID ID)
+{
+    return g_Str_English[ID];
+};
diff --git a/Ventoy2Disk/Ventoy2Disk/Language.h b/Ventoy2Disk/Ventoy2Disk/Language.h
new file mode 100644 (file)
index 0000000..478188f
--- /dev/null
@@ -0,0 +1,57 @@
+/******************************************************************************
+ * Language.h
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef __LANGUAGE_H__
+#define __LANGUAGE_H__
+
+typedef enum STR_ID
+{
+    STR_ERROR = 0,
+    STR_WARNING,
+    STR_INFO,
+    STR_INCORRECT_DIR,
+
+    STR_DEVICE,
+    STR_LOCAL_VER,
+    STR_DISK_VER,
+    STR_STATUS,
+    STR_INSTALL,
+    STR_UPDATE,
+
+    STR_UPDATE_TIP,
+    STR_INSTALL_TIP,
+    STR_INSTALL_TIP2,
+
+    STR_INSTALL_SUCCESS,
+    STR_INSTALL_FAILED,
+    STR_UPDATE_SUCCESS,
+    STR_UPDATE_FAILED,
+
+    STR_WAIT_PROCESS,
+
+
+    STR_ID_MAX
+}STR_ID;
+
+const TCHAR * GetString(enum STR_ID ID);
+
+#define _G(a) GetString(a)
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/PhyDrive.c b/Ventoy2Disk/Ventoy2Disk/PhyDrive.c
new file mode 100644 (file)
index 0000000..6b579ed
--- /dev/null
@@ -0,0 +1,1458 @@
+/******************************************************************************
+ * PhyDrive.c
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <Windows.h>
+#include <winternl.h>
+#include <commctrl.h>
+#include <initguid.h>
+#include <vds.h>
+#include "resource.h"
+#include "Language.h"
+#include "Ventoy2Disk.h"
+#include "fat_filelib.h"
+#include "ff.h"
+
+#define VDS_SET_ERROR SetLastError
+#define IVdsServiceLoader_LoadService(This, pwszMachineName, ppService) (This)->lpVtbl->LoadService(This, pwszMachineName, ppService)
+#define IVdsServiceLoader_Release(This) (This)->lpVtbl->Release(This)
+#define IVdsService_QueryProviders(This, masks, ppEnum) (This)->lpVtbl->QueryProviders(This, masks, ppEnum)
+#define IVdsService_WaitForServiceReady(This) ((This)->lpVtbl->WaitForServiceReady(This))
+#define IVdsService_CleanupObsoleteMountPoints(This) ((This)->lpVtbl->CleanupObsoleteMountPoints(This))
+#define IVdsService_Refresh(This) ((This)->lpVtbl->Refresh(This))
+#define IVdsService_Reenumerate(This) ((This)->lpVtbl->Reenumerate(This)) 
+#define IVdsSwProvider_QueryInterface(This, riid, ppvObject) (This)->lpVtbl->QueryInterface(This, riid, ppvObject)
+#define IVdsProvider_Release(This) (This)->lpVtbl->Release(This)
+#define IVdsSwProvider_QueryPacks(This, ppEnum) (This)->lpVtbl->QueryPacks(This, ppEnum)
+#define IVdsSwProvider_Release(This) (This)->lpVtbl->Release(This)
+#define IVdsPack_QueryDisks(This, ppEnum) (This)->lpVtbl->QueryDisks(This, ppEnum)
+#define IVdsDisk_GetProperties(This, pDiskProperties) (This)->lpVtbl->GetProperties(This, pDiskProperties)
+#define IVdsDisk_Release(This) (This)->lpVtbl->Release(This)
+#define IVdsDisk_QueryInterface(This, riid, ppvObject) (This)->lpVtbl->QueryInterface(This, riid, ppvObject)
+#define IVdsAdvancedDisk_QueryPartitions(This, ppPartitionPropArray, plNumberOfPartitions) (This)->lpVtbl->QueryPartitions(This, ppPartitionPropArray, plNumberOfPartitions)
+#define IVdsAdvancedDisk_DeletePartition(This, ullOffset, bForce, bForceProtected) (This)->lpVtbl->DeletePartition(This, ullOffset, bForce, bForceProtected)
+#define IVdsAdvancedDisk_Clean(This, bForce, bForceOEM, bFullClean, ppAsync) (This)->lpVtbl->Clean(This, bForce, bForceOEM, bFullClean, ppAsync)
+#define IVdsAdvancedDisk_Release(This) (This)->lpVtbl->Release(This)
+#define IEnumVdsObject_Next(This, celt, ppObjectArray, pcFetched) (This)->lpVtbl->Next(This, celt, ppObjectArray, pcFetched)
+#define IVdsPack_QueryVolumes(This, ppEnum) (This)->lpVtbl->QueryVolumes(This, ppEnum)
+#define IVdsVolume_QueryInterface(This, riid, ppvObject) (This)->lpVtbl->QueryInterface(This, riid, ppvObject)
+#define IVdsVolume_Release(This) (This)->lpVtbl->Release(This)
+#define IVdsVolumeMF3_QueryVolumeGuidPathnames(This, pwszPathArray, pulNumberOfPaths) (This)->lpVtbl->QueryVolumeGuidPathnames(This,pwszPathArray,pulNumberOfPaths)
+#define IVdsVolumeMF3_FormatEx2(This, pwszFileSystemTypeName, usFileSystemRevision, ulDesiredUnitAllocationSize, pwszLabel, Options, ppAsync) (This)->lpVtbl->FormatEx2(This, pwszFileSystemTypeName, usFileSystemRevision, ulDesiredUnitAllocationSize, pwszLabel, Options, ppAsync)
+#define IVdsVolumeMF3_Release(This) (This)->lpVtbl->Release(This)
+#define IVdsVolume_GetProperties(This, pVolumeProperties) (This)->lpVtbl->GetProperties(This,pVolumeProperties)
+#define IVdsAsync_Cancel(This) (This)->lpVtbl->Cancel(This)
+#define IVdsAsync_QueryStatus(This,pHrResult,pulPercentCompleted) (This)->lpVtbl->QueryStatus(This,pHrResult,pulPercentCompleted)
+#define IVdsAsync_Wait(This,pHrResult,pAsyncOut) (This)->lpVtbl->Wait(This,pHrResult,pAsyncOut)
+#define IVdsAsync_Release(This) (This)->lpVtbl->Release(This)
+
+#define IUnknown_QueryInterface(This, a, b) (This)->lpVtbl->QueryInterface(This,a,b)
+#define IUnknown_Release(This) (This)->lpVtbl->Release(This)
+
+/*
+* Delete all the partitions from a disk, using VDS
+* Mostly copied from https://social.msdn.microsoft.com/Forums/vstudio/en-US/b90482ae-4e44-4b08-8731-81915030b32a/createpartition-using-vds-interface-throw-error-enointerface-dcom?forum=vcgeneral
+*/
+BOOL DeletePartitions(DWORD DriveIndex, BOOL OnlyPart2)
+{
+    BOOL r = FALSE;
+    HRESULT hr;
+    ULONG ulFetched;
+    wchar_t wPhysicalName[48];
+    IVdsServiceLoader *pLoader;
+    IVdsService *pService;
+    IEnumVdsObject *pEnum;
+    IUnknown *pUnk;
+
+    swprintf_s(wPhysicalName, ARRAYSIZE(wPhysicalName), L"\\\\?\\PhysicalDrive%lu", DriveIndex);
+
+    // Initialize COM
+    CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
+    CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_CONNECT,
+        RPC_C_IMP_LEVEL_IMPERSONATE, NULL, 0, NULL);
+
+    // Create a VDS Loader Instance
+    hr = CoCreateInstance(&CLSID_VdsLoader, NULL, CLSCTX_LOCAL_SERVER | CLSCTX_REMOTE_SERVER,
+        &IID_IVdsServiceLoader, (void **)&pLoader);
+    if (hr != S_OK) {
+        VDS_SET_ERROR(hr);
+        Log("Could not create VDS Loader Instance: %u", LASTERR);
+        goto out;
+    }
+
+    // Load the VDS Service
+    hr = IVdsServiceLoader_LoadService(pLoader, L"", &pService);
+    IVdsServiceLoader_Release(pLoader);
+    if (hr != S_OK) {
+        VDS_SET_ERROR(hr);
+        Log("Could not load VDS Service: %u", LASTERR);
+        goto out;
+    }
+
+    // Wait for the Service to become ready if needed
+    hr = IVdsService_WaitForServiceReady(pService);
+    if (hr != S_OK) {
+        VDS_SET_ERROR(hr);
+        Log("VDS Service is not ready: %u", LASTERR);
+        goto out;
+    }
+
+    // Query the VDS Service Providers
+    hr = IVdsService_QueryProviders(pService, VDS_QUERY_SOFTWARE_PROVIDERS, &pEnum);
+    if (hr != S_OK) {
+        VDS_SET_ERROR(hr);
+        Log("Could not query VDS Service Providers: %u", LASTERR);
+        goto out;
+    }
+
+    while (IEnumVdsObject_Next(pEnum, 1, &pUnk, &ulFetched) == S_OK) {
+        IVdsProvider *pProvider;
+        IVdsSwProvider *pSwProvider;
+        IEnumVdsObject *pEnumPack;
+        IUnknown *pPackUnk;
+
+        // Get VDS Provider
+        hr = IUnknown_QueryInterface(pUnk, &IID_IVdsProvider, (void **)&pProvider);
+        IUnknown_Release(pUnk);
+        if (hr != S_OK) {
+            VDS_SET_ERROR(hr);
+            Log("Could not get VDS Provider: %u", LASTERR);
+            goto out;
+        }
+
+        // Get VDS Software Provider
+        hr = IVdsSwProvider_QueryInterface(pProvider, &IID_IVdsSwProvider, (void **)&pSwProvider);
+        IVdsProvider_Release(pProvider);
+        if (hr != S_OK) {
+            VDS_SET_ERROR(hr);
+            Log("Could not get VDS Software Provider: %u", LASTERR);
+            goto out;
+        }
+
+        // Get VDS Software Provider Packs
+        hr = IVdsSwProvider_QueryPacks(pSwProvider, &pEnumPack);
+        IVdsSwProvider_Release(pSwProvider);
+        if (hr != S_OK) {
+            VDS_SET_ERROR(hr);
+            Log("Could not get VDS Software Provider Packs: %u", LASTERR);
+            goto out;
+        }
+
+        // Enumerate Provider Packs
+        while (IEnumVdsObject_Next(pEnumPack, 1, &pPackUnk, &ulFetched) == S_OK) {
+            IVdsPack *pPack;
+            IEnumVdsObject *pEnumDisk;
+            IUnknown *pDiskUnk;
+
+            hr = IUnknown_QueryInterface(pPackUnk, &IID_IVdsPack, (void **)&pPack);
+            IUnknown_Release(pPackUnk);
+            if (hr != S_OK) {
+                VDS_SET_ERROR(hr);
+                Log("Could not query VDS Software Provider Pack: %u", LASTERR);
+                goto out;
+            }
+
+            // Use the pack interface to access the disks
+            hr = IVdsPack_QueryDisks(pPack, &pEnumDisk);
+            if (hr != S_OK) {
+                VDS_SET_ERROR(hr);
+                Log("Could not query VDS disks: %u", LASTERR);
+                goto out;
+            }
+
+            // List disks
+            while (IEnumVdsObject_Next(pEnumDisk, 1, &pDiskUnk, &ulFetched) == S_OK) {
+                VDS_DISK_PROP diskprop;
+                VDS_PARTITION_PROP* prop_array;
+                LONG i, prop_array_size;
+                IVdsDisk *pDisk;
+                IVdsAdvancedDisk *pAdvancedDisk;
+
+                // Get the disk interface.
+                hr = IUnknown_QueryInterface(pDiskUnk, &IID_IVdsDisk, (void **)&pDisk);
+                if (hr != S_OK) {
+                    VDS_SET_ERROR(hr);
+                    Log("Could not query VDS Disk Interface: %u", LASTERR);
+                    goto out;
+                }
+
+                // Get the disk properties
+                hr = IVdsDisk_GetProperties(pDisk, &diskprop);
+                if (hr != S_OK) {
+                    VDS_SET_ERROR(hr);
+                    Log("Could not query VDS Disk Properties: %u", LASTERR);
+                    goto out;
+                }
+
+                // Isolate the disk we want
+                if (_wcsicmp(wPhysicalName, diskprop.pwszName) != 0) {
+                    IVdsDisk_Release(pDisk);
+                    continue;
+                }
+
+                // Instantiate the AdvanceDisk interface for our disk.
+                hr = IVdsDisk_QueryInterface(pDisk, &IID_IVdsAdvancedDisk, (void **)&pAdvancedDisk);
+                IVdsDisk_Release(pDisk);
+                if (hr != S_OK) {
+                    VDS_SET_ERROR(hr);
+                    Log("Could not access VDS Advanced Disk interface: %u", LASTERR);
+                    goto out;
+                }
+
+                // Query the partition data, so we can get the start offset, which we need for deletion
+                hr = IVdsAdvancedDisk_QueryPartitions(pAdvancedDisk, &prop_array, &prop_array_size);
+                if (hr == S_OK) {
+                    Log("Deleting ALL partition(s) from disk '%S':", diskprop.pwszName);
+                    // Now go through each partition
+                    for (i = 0; i < prop_array_size; i++) {
+                        
+                        Log("* Partition %d (offset: %lld, size: %llu)", prop_array[i].ulPartitionNumber,
+                            prop_array[i].ullOffset, (ULONGLONG)prop_array[i].ullSize);
+
+                        if (OnlyPart2 && prop_array[i].ullOffset == 2048*512)
+                        {
+                            Log("Skip this partition...");
+                            continue;
+                        }
+
+
+                        hr = IVdsAdvancedDisk_DeletePartition(pAdvancedDisk, prop_array[i].ullOffset, TRUE, TRUE);
+                        if (hr != S_OK) {
+                            r = FALSE;
+                            VDS_SET_ERROR(hr);
+                            Log("Could not delete partitions: %u", LASTERR);
+                        }
+                    }
+                    r = TRUE;
+                }
+                else {
+                    Log("No partition to delete on disk '%S'", diskprop.pwszName);
+                    r = TRUE;
+                }
+                CoTaskMemFree(prop_array);
+
+#if 0
+                // Issue a Clean while we're at it
+                HRESULT hr2 = E_FAIL;
+                ULONG completed;
+                IVdsAsync* pAsync;
+                hr = IVdsAdvancedDisk_Clean(pAdvancedDisk, TRUE, FALSE, FALSE, &pAsync);
+                while (SUCCEEDED(hr)) {
+                    if (IS_ERROR(FormatStatus)) {
+                        IVdsAsync_Cancel(pAsync);
+                        break;
+                    }
+                    hr = IVdsAsync_QueryStatus(pAsync, &hr2, &completed);
+                    if (SUCCEEDED(hr)) {
+                        hr = hr2;
+                        if (hr == S_OK)
+                            break;
+                        if (hr == VDS_E_OPERATION_PENDING)
+                            hr = S_OK;
+                    }
+                    Sleep(500);
+                }
+                if (hr != S_OK) {
+                    VDS_SET_ERROR(hr);
+                    Log("Could not clean disk: %s", LASTERR);
+                }
+#endif
+                IVdsAdvancedDisk_Release(pAdvancedDisk);
+                goto out;
+            }
+        }
+    }
+
+out:
+    return r;
+}
+
+
+static DWORD GetVentoyVolumeName(int PhyDrive, UINT32 StartSectorId, CHAR *NameBuf, UINT32 BufLen, BOOL DelSlash)
+{
+    size_t len;
+    BOOL bRet;
+    DWORD dwSize;
+    HANDLE hDrive;
+    HANDLE hVolume;
+    UINT64 PartOffset;
+    DWORD Status = ERROR_NOT_FOUND;
+    DISK_EXTENT *pExtents = NULL;
+    CHAR VolumeName[MAX_PATH] = { 0 };
+    VOLUME_DISK_EXTENTS DiskExtents;
+
+    PartOffset = 512ULL * StartSectorId;
+
+    Log("GetVentoyVolumeName PhyDrive %d PartOffset:%llu", PhyDrive, (ULONGLONG)PartOffset);
+
+    hVolume = FindFirstVolumeA(VolumeName, sizeof(VolumeName));
+    if (hVolume == INVALID_HANDLE_VALUE)
+    {
+        return 1;
+    }
+
+    do {
+
+        len = strlen(VolumeName);
+        Log("Find volume:%s", VolumeName);
+
+        VolumeName[len - 1] = 0;
+
+        hDrive = CreateFileA(VolumeName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
+        if (hDrive == INVALID_HANDLE_VALUE)
+        {
+            continue;
+        }
+
+        bRet = DeviceIoControl(hDrive,
+            IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
+            NULL,
+            0,
+            &DiskExtents,
+            (DWORD)(sizeof(DiskExtents)),
+            (LPDWORD)&dwSize,
+            NULL);
+
+        Log("IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS bRet:%u code:%u", bRet, LASTERR);
+        Log("NumberOfDiskExtents:%u DiskNumber:%u", DiskExtents.NumberOfDiskExtents, DiskExtents.Extents[0].DiskNumber);
+
+        if (bRet && DiskExtents.NumberOfDiskExtents == 1)
+        {
+            pExtents = DiskExtents.Extents;
+
+            Log("This volume DiskNumber:%u offset:%llu", pExtents->DiskNumber, (ULONGLONG)pExtents->StartingOffset.QuadPart);
+            if ((int)pExtents->DiskNumber == PhyDrive && pExtents->StartingOffset.QuadPart == PartOffset)
+            {
+                Log("This volume match");
+
+                if (!DelSlash)
+                {
+                    VolumeName[len - 1] = '\\';
+                }
+
+                sprintf_s(NameBuf, BufLen, "%s", VolumeName);
+                Status = ERROR_SUCCESS;
+                CloseHandle(hDrive);
+                break;
+            }
+        }
+
+        CloseHandle(hDrive);
+    } while (FindNextVolumeA(hVolume, VolumeName, sizeof(VolumeName)));
+
+    FindVolumeClose(hVolume);
+
+    Log("GetVentoyVolumeName return %u", Status);
+    return Status;
+}
+
+static int GetLettersBelongPhyDrive(int PhyDrive, char *DriveLetters, size_t Length)
+{
+    int n = 0;
+    DWORD DataSize = 0;
+    CHAR *Pos = NULL;
+    CHAR *StringBuf = NULL;
+
+    DataSize = GetLogicalDriveStringsA(0, NULL);
+    StringBuf = (CHAR *)malloc(DataSize + 1);
+    if (StringBuf == NULL)
+    {
+        return 1;
+    }
+
+    GetLogicalDriveStringsA(DataSize, StringBuf);
+
+    for (Pos = StringBuf; *Pos; Pos += strlen(Pos) + 1)
+    {
+        if (n < (int)Length && PhyDrive == GetPhyDriveByLogicalDrive(Pos[0]))
+        {
+            Log("%C: is belong to phydrive%d", Pos[0], PhyDrive);
+            DriveLetters[n++] = Pos[0];
+        }
+    }
+
+    free(StringBuf);
+    return 0;
+}
+
+static HANDLE GetPhysicalHandle(int Drive, BOOLEAN bLockDrive, BOOLEAN bWriteAccess, BOOLEAN bWriteShare)
+{
+    int i;
+    DWORD dwSize;
+    DWORD LastError;
+    UINT64 EndTime;
+    HANDLE hDrive = INVALID_HANDLE_VALUE;
+    CHAR PhyDrive[128];
+    CHAR DevPath[MAX_PATH] = { 0 };
+
+    safe_sprintf(PhyDrive, "\\\\.\\PhysicalDrive%d", Drive);
+
+    if (0 == QueryDosDeviceA(PhyDrive + 4, DevPath, sizeof(DevPath)))
+    {
+        Log("QueryDosDeviceA failed error:%u", GetLastError());
+        strcpy_s(DevPath, sizeof(DevPath), "???");
+    }
+    else
+    {
+        Log("QueryDosDeviceA success %s", DevPath);
+    }
+
+    for (i = 0; i < DRIVE_ACCESS_RETRIES; i++)
+    {
+        // Try without FILE_SHARE_WRITE (unless specifically requested) so that
+        // we won't be bothered by the OS or other apps when we set up our data.
+        // However this means we might have to wait for an access gap...
+        // We keep FILE_SHARE_READ though, as this shouldn't hurt us any, and is
+        // required for enumeration.
+        hDrive = CreateFileA(PhyDrive,
+            GENERIC_READ | (bWriteAccess ? GENERIC_WRITE : 0),
+            FILE_SHARE_READ | (bWriteShare ? FILE_SHARE_WRITE : 0),
+            NULL,
+            OPEN_EXISTING,
+            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH,
+            NULL);
+
+        LastError = GetLastError();
+        Log("[%d] CreateFileA %s code:%u %p", i, PhyDrive, LastError, hDrive);
+
+        if (hDrive != INVALID_HANDLE_VALUE)
+        {
+            break;
+        }
+
+        if ((LastError != ERROR_SHARING_VIOLATION) && (LastError != ERROR_ACCESS_DENIED))
+        {
+            break;
+        }
+
+        if (i == 0)
+        {
+            Log("Waiting for access on %s [%s]...", PhyDrive, DevPath);
+        }
+        else if (!bWriteShare && (i > DRIVE_ACCESS_RETRIES / 3))
+        {
+            // If we can't seem to get a hold of the drive for some time, try to enable FILE_SHARE_WRITE...
+            Log("Warning: Could not obtain exclusive rights. Retrying with write sharing enabled...");
+            bWriteShare = TRUE;
+
+            // Try to report the process that is locking the drive
+            // We also use bit 6 as a flag to indicate that SearchProcess was called.
+            //access_mask = SearchProcess(DevPath, SEARCH_PROCESS_TIMEOUT, TRUE, TRUE, FALSE) | 0x40;
+
+        }
+        Sleep(DRIVE_ACCESS_TIMEOUT / DRIVE_ACCESS_RETRIES);
+    }
+
+    if (hDrive == INVALID_HANDLE_VALUE)
+    {
+        Log("Could not open %s %u", PhyDrive, LASTERR);
+        goto End;
+    }
+
+    if (bWriteAccess)
+    {
+        Log("Opened %s for %s write access", PhyDrive, bWriteShare ? "shared" : "exclusive");
+    }
+
+    if (bLockDrive)
+    {
+        if (DeviceIoControl(hDrive, FSCTL_ALLOW_EXTENDED_DASD_IO, NULL, 0, NULL, 0, &dwSize, NULL))
+        {
+            Log("I/O boundary checks disabled");
+        }
+
+        EndTime = GetTickCount64() + DRIVE_ACCESS_TIMEOUT;
+
+        do {
+            if (DeviceIoControl(hDrive, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL))
+            {
+                Log("FSCTL_LOCK_VOLUME success");
+                goto End;
+            }
+            Sleep(DRIVE_ACCESS_TIMEOUT / DRIVE_ACCESS_RETRIES);
+        } while (GetTickCount64() < EndTime);
+
+        // If we reached this section, either we didn't manage to get a lock or the user cancelled
+        Log("Could not lock access to %s %u", PhyDrive, LASTERR);
+
+        // See if we can report the processes are accessing the drive
+        //if (!IS_ERROR(FormatStatus) && (access_mask == 0))
+        //    access_mask = SearchProcess(DevPath, SEARCH_PROCESS_TIMEOUT, TRUE, TRUE, FALSE);
+        // Try to continue if the only access rights we saw were for read-only
+        //if ((access_mask & 0x07) != 0x01)
+        //    safe_closehandle(hDrive);
+
+        CHECK_CLOSE_HANDLE(hDrive);
+    }
+
+End:
+
+    if (hDrive == INVALID_HANDLE_VALUE)
+    {
+        Log("Can get handle of %s, maybe some process control it.", DevPath);
+    }
+
+    return hDrive;
+}
+
+int GetPhyDriveByLogicalDrive(int DriveLetter)
+{
+    BOOL Ret;
+    DWORD dwSize;
+    HANDLE Handle;
+    VOLUME_DISK_EXTENTS DiskExtents;
+    CHAR PhyPath[128];
+
+    safe_sprintf(PhyPath, "\\\\.\\%C:", (CHAR)DriveLetter);
+
+    Handle = CreateFileA(PhyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
+    if (Handle == INVALID_HANDLE_VALUE)
+    {
+        Log("Could not open the disk<%s>, error:%u", PhyPath, LASTERR);
+        return -1;
+    }
+
+    Ret = DeviceIoControl(Handle,
+        IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
+        NULL,
+        0,
+        &DiskExtents,
+        (DWORD)(sizeof(DiskExtents)),
+        (LPDWORD)&dwSize,
+        NULL);
+
+    if (!Ret || DiskExtents.NumberOfDiskExtents == 0)
+    {
+        Log("DeviceIoControl IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS failed %s, error:%u", PhyPath, LASTERR);
+        CHECK_CLOSE_HANDLE(Handle);
+        return -1;
+    }
+    CHECK_CLOSE_HANDLE(Handle);
+
+    Log("LogicalDrive:%s PhyDrive:%d Offset:%llu ExtentLength:%llu",
+        PhyPath,
+        DiskExtents.Extents[0].DiskNumber,
+        DiskExtents.Extents[0].StartingOffset.QuadPart,
+        DiskExtents.Extents[0].ExtentLength.QuadPart
+        );
+
+    return (int)DiskExtents.Extents[0].DiskNumber;
+}
+
+int GetAllPhysicalDriveInfo(PHY_DRIVE_INFO *pDriveList, DWORD *pDriveCount)
+{
+    int i;
+    int Count;
+    int id;
+    int Letter = 'A';
+    BOOL  bRet;
+    DWORD dwBytes;
+    DWORD DriveCount = 0;
+    HANDLE Handle = INVALID_HANDLE_VALUE;
+    CHAR PhyDrive[128];
+    PHY_DRIVE_INFO *CurDrive = pDriveList;
+    GET_LENGTH_INFORMATION LengthInfo;
+    STORAGE_PROPERTY_QUERY Query;
+    STORAGE_DESCRIPTOR_HEADER DevDescHeader;
+    STORAGE_DEVICE_DESCRIPTOR *pDevDesc;
+    int PhyDriveId[VENTOY_MAX_PHY_DRIVE];
+
+    Count = GetPhysicalDriveCount();
+
+    for (i = 0; i < Count && i < VENTOY_MAX_PHY_DRIVE; i++)
+    {
+        PhyDriveId[i] = i;
+    }
+
+    dwBytes = GetLogicalDrives();
+    Log("Logical Drives: 0x%x", dwBytes);
+    while (dwBytes)
+    {
+        if (dwBytes & 0x01)
+        {
+            id = GetPhyDriveByLogicalDrive(Letter);
+            Log("%C --> %d", Letter, id);
+            if (id >= 0)
+            {
+                for (i = 0; i < Count; i++)
+                {
+                    if (PhyDriveId[i] == id)
+                    {
+                        break;
+                    }
+                }
+
+                if (i >= Count)
+                {
+                    Log("Add phy%d to list", i);
+                    PhyDriveId[Count] = id;
+                    Count++;
+                }
+            }
+        }
+
+        Letter++;
+        dwBytes >>= 1;
+    }
+
+    for (i = 0; i < Count && DriveCount < VENTOY_MAX_PHY_DRIVE; i++)
+    {
+        CHECK_CLOSE_HANDLE(Handle);
+
+        safe_sprintf(PhyDrive, "\\\\.\\PhysicalDrive%d", PhyDriveId[i]);
+        Handle = CreateFileA(PhyDrive, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);        
+        Log("Create file Handle:%p %s status:%u", Handle, PhyDrive, LASTERR);
+
+        if (Handle == INVALID_HANDLE_VALUE)
+        {
+            continue;
+        }
+
+        bRet = DeviceIoControl(Handle,
+                               IOCTL_DISK_GET_LENGTH_INFO, NULL,
+                               0,
+                               &LengthInfo,
+                               sizeof(LengthInfo),
+                               &dwBytes,
+                               NULL);
+        if (!bRet)
+        {
+            Log("DeviceIoControl IOCTL_DISK_GET_LENGTH_INFO failed error:%u", LASTERR);
+            continue;
+        }
+
+        Log("PHYSICALDRIVE%d size %llu bytes", i, (ULONGLONG)LengthInfo.Length.QuadPart);
+
+        Query.PropertyId = StorageDeviceProperty;
+        Query.QueryType = PropertyStandardQuery;
+
+        bRet = DeviceIoControl(Handle,
+                               IOCTL_STORAGE_QUERY_PROPERTY,
+                               &Query,
+                               sizeof(Query),
+                               &DevDescHeader,
+                               sizeof(STORAGE_DESCRIPTOR_HEADER),
+                               &dwBytes,
+                               NULL);
+        if (!bRet)
+        {
+            Log("DeviceIoControl1 error:%u dwBytes:%u", LASTERR, dwBytes);
+            continue;
+        }
+
+        pDevDesc = (STORAGE_DEVICE_DESCRIPTOR *)malloc(DevDescHeader.Size);
+        if (!pDevDesc)
+        {
+            Log("failed to malloc error:%u len:%u", LASTERR, DevDescHeader.Size);
+            continue;
+        }
+
+        bRet = DeviceIoControl(Handle,
+                               IOCTL_STORAGE_QUERY_PROPERTY,
+                               &Query,
+                               sizeof(Query),
+                               pDevDesc,
+                               DevDescHeader.Size,
+                               &dwBytes,
+                               NULL);
+        if (!bRet)
+        {
+            Log("DeviceIoControl2 error:%u dwBytes:%u", LASTERR, dwBytes);
+            free(pDevDesc);
+            continue;
+        }
+
+        CurDrive->PhyDrive = i;
+        CurDrive->SizeInBytes = LengthInfo.Length.QuadPart;
+        CurDrive->DeviceType = pDevDesc->DeviceType;
+        CurDrive->RemovableMedia = pDevDesc->RemovableMedia;
+        CurDrive->BusType = pDevDesc->BusType;
+
+        if (pDevDesc->VendorIdOffset)
+        {
+            safe_strcpy(CurDrive->VendorId, (char *)pDevDesc + pDevDesc->VendorIdOffset);
+            TrimString(CurDrive->VendorId);
+        }
+
+        if (pDevDesc->ProductIdOffset)
+        {
+            safe_strcpy(CurDrive->ProductId, (char *)pDevDesc + pDevDesc->ProductIdOffset);
+            TrimString(CurDrive->ProductId);
+        }
+
+        if (pDevDesc->ProductRevisionOffset)
+        {
+            safe_strcpy(CurDrive->ProductRev, (char *)pDevDesc + pDevDesc->ProductRevisionOffset);
+            TrimString(CurDrive->ProductRev);
+        }
+
+        if (pDevDesc->SerialNumberOffset)
+        {
+            safe_strcpy(CurDrive->SerialNumber, (char *)pDevDesc + pDevDesc->SerialNumberOffset);
+            TrimString(CurDrive->SerialNumber);
+        }
+
+        CurDrive++;
+        DriveCount++;
+
+        free(pDevDesc);
+
+        CHECK_CLOSE_HANDLE(Handle);
+    }
+
+    for (i = 0, CurDrive = pDriveList; i < (int)DriveCount; i++, CurDrive++)
+    {
+        Log("PhyDrv:%d BusType:%-4s Removable:%u Size:%dGB(%llu) Name:%s %s",
+            CurDrive->PhyDrive, GetBusTypeString(CurDrive->BusType), CurDrive->RemovableMedia,
+            GetHumanReadableGBSize(CurDrive->SizeInBytes), CurDrive->SizeInBytes,
+            CurDrive->VendorId, CurDrive->ProductId);
+    }
+
+    *pDriveCount = DriveCount;
+
+    return 0;
+}
+
+
+static HANDLE g_FatPhyDrive;
+static UINT64 g_Part2StartSec;
+static int GetVentoyVersionFromFatFile(CHAR *VerBuf, size_t BufLen)
+{
+    int rc = 1;
+    int size = 0;
+    char *buf = NULL;
+    void *flfile = NULL;
+
+    flfile = fl_fopen("/grub/grub.cfg", "rb");
+    if (flfile)
+    {
+        fl_fseek(flfile, 0, SEEK_END);
+        size = (int)fl_ftell(flfile);
+
+        fl_fseek(flfile, 0, SEEK_SET);
+
+        buf = (char *)malloc(size + 1);
+        if (buf)
+        {
+            fl_fread(buf, 1, size, flfile);
+            buf[size] = 0;
+
+            rc = 0;
+            sprintf_s(VerBuf, BufLen, "%s", ParseVentoyVersionFromString(buf));
+            free(buf);
+        }
+
+        fl_fclose(flfile);
+    }
+
+    return rc;
+}
+
+static int VentoyFatDiskRead(uint32 Sector, uint8 *Buffer, uint32 SectorCount)
+{
+    DWORD dwSize;
+    BOOL bRet;
+    DWORD ReadSize;
+    LARGE_INTEGER liCurrentPosition;
+
+    liCurrentPosition.QuadPart = Sector + g_Part2StartSec;
+    liCurrentPosition.QuadPart *= 512;
+    SetFilePointerEx(g_FatPhyDrive, liCurrentPosition, &liCurrentPosition, FILE_BEGIN);
+
+    ReadSize = (DWORD)(SectorCount * 512);
+
+    bRet = ReadFile(g_FatPhyDrive, Buffer, ReadSize, &dwSize, NULL);
+    if (bRet == FALSE || dwSize != ReadSize)
+    {
+        Log("ReadFile error bRet:%u WriteSize:%u dwSize:%u ErrCode:%u\n", bRet, ReadSize, dwSize, LASTERR);
+    }
+
+    return 1;
+}
+
+
+int GetVentoyVerInPhyDrive(const PHY_DRIVE_INFO *pDriveInfo, CHAR *VerBuf, size_t BufLen)
+{
+    int rc = 0;
+    HANDLE hDrive;
+
+    hDrive = GetPhysicalHandle(pDriveInfo->PhyDrive, FALSE, FALSE, FALSE);
+    if (hDrive == INVALID_HANDLE_VALUE)
+    {
+        return 1;
+    }
+    
+    g_FatPhyDrive = hDrive;
+    g_Part2StartSec = (pDriveInfo->SizeInBytes - VENTOY_EFI_PART_SIZE) / 512;
+
+    Log("Parse FAT fs...");
+
+    fl_init();
+
+    if (0 == fl_attach_media(VentoyFatDiskRead, NULL))
+    {
+        rc = GetVentoyVersionFromFatFile(VerBuf, BufLen);
+    }
+    else
+    {
+        rc = 1;
+    }
+
+    fl_shutdown();
+
+    CHECK_CLOSE_HANDLE(hDrive);
+
+    return rc;
+}
+
+
+
+
+static unsigned int g_disk_unxz_len = 0;
+static BYTE *g_part_img_pos = NULL;
+static BYTE *g_part_img_buf[VENTOY_EFI_PART_SIZE / SIZE_1MB];
+
+static int disk_xz_flush(void *src, unsigned int size)
+{
+    unsigned int i;
+    BYTE *buf = (BYTE *)src;
+
+    for (i = 0; i < size; i++)
+    {
+        *g_part_img_pos = *buf++;
+
+        g_disk_unxz_len++;
+        if ((g_disk_unxz_len % SIZE_1MB) == 0)
+        {
+            g_part_img_pos = g_part_img_buf[g_disk_unxz_len / SIZE_1MB];
+        }
+        else
+        {
+            g_part_img_pos++;
+        }
+    }
+
+    return (int)size;
+}
+
+static void unxz_error(char *x)
+{
+    Log("%s", x);
+}
+
+static BOOL TryWritePart2(HANDLE hDrive, UINT64 StartSectorId)
+{
+    BOOL bRet;
+    DWORD TrySize = 16 * 1024;
+    DWORD dwSize;
+    BYTE *Buffer = NULL;
+    unsigned char *data = NULL;
+    LARGE_INTEGER liCurrentPosition;
+
+    liCurrentPosition.QuadPart = StartSectorId * 512;
+    SetFilePointerEx(hDrive, liCurrentPosition, &liCurrentPosition, FILE_BEGIN);
+    
+    Buffer = malloc(TrySize);
+
+    bRet = WriteFile(hDrive, Buffer, TrySize, &dwSize, NULL);
+
+    free(Buffer);
+
+    Log("Try write part2 bRet:%u dwSize:%u code:%u", bRet, dwSize, LASTERR);
+
+    if (bRet && dwSize == TrySize)
+    {
+        return TRUE;
+    }
+
+    return FALSE;
+}
+
+static int FormatPart2Fat(HANDLE hDrive, UINT64 StartSectorId)
+{
+    int i;
+    int rc = 0;
+    int len = 0;
+    int writelen = 0;
+    int partwrite = 0;
+    DWORD dwSize = 0;
+    BOOL bRet;
+    unsigned char *data = NULL;
+    LARGE_INTEGER liCurrentPosition;
+
+    Log("FormatPart2Fat ...");
+
+    rc = ReadWholeFileToBuf(VENTOY_FILE_DISK_IMG, 0, (void **)&data, &len);
+    if (rc)
+    {
+        Log("Failed to read img file %p %u", data, len);
+        return 1;
+    }
+
+    liCurrentPosition.QuadPart = StartSectorId * 512;
+    SetFilePointerEx(hDrive, liCurrentPosition, &liCurrentPosition, FILE_BEGIN);
+
+    memset(g_part_img_buf, 0, sizeof(g_part_img_buf));
+
+    g_part_img_buf[0] = (BYTE *)malloc(VENTOY_EFI_PART_SIZE);
+    if (g_part_img_buf[0])
+    {
+        Log("Malloc whole img buffer success, now decompress ...");
+        unxz(data, len, NULL, NULL, g_part_img_buf[0], &writelen, unxz_error);
+
+        if (len == writelen)
+        {
+            Log("decompress finished success");
+            for (i = 0; i < VENTOY_EFI_PART_SIZE / SIZE_1MB; i++)
+            {
+                dwSize = 0;
+                bRet = WriteFile(hDrive, g_part_img_buf[0] + i * SIZE_1MB, SIZE_1MB, &dwSize, NULL);
+                Log("Write part data bRet:%u dwSize:%u code:%u", bRet, dwSize, LASTERR);
+
+                if (!bRet)
+                {
+                    rc = 1;
+                    goto End;
+                }
+
+                PROGRESS_BAR_SET_POS(PT_WRITE_VENTOY_START + i);                
+            }
+        }
+        else
+        {
+            rc = 1;
+            Log("decompress finished failed");
+            goto End;
+        }
+    }
+    else
+    {
+        Log("Failed to malloc whole img size %u, now split it", VENTOY_EFI_PART_SIZE);
+
+        partwrite = 1;
+        for (i = 0; i < VENTOY_EFI_PART_SIZE / SIZE_1MB; i++)
+        {
+            g_part_img_buf[i] = (BYTE *)malloc(SIZE_1MB);
+            if (g_part_img_buf[i] == NULL)
+            {
+                rc = 1;
+                goto End;
+            }
+        }
+
+        Log("Malloc part img buffer success, now decompress ...");
+
+        g_part_img_pos = g_part_img_buf[0];
+
+        unxz(data, len, NULL, disk_xz_flush, NULL, NULL, unxz_error);
+
+        if (g_disk_unxz_len == VENTOY_EFI_PART_SIZE)
+        {
+            Log("decompress finished success");
+            for (int i = 0; i < VENTOY_EFI_PART_SIZE / SIZE_1MB; i++)
+            {
+                dwSize = 0;
+                bRet = WriteFile(hDrive, g_part_img_buf[i], SIZE_1MB, &dwSize, NULL);
+                Log("Write part data bRet:%u dwSize:%u code:%u", bRet, dwSize, LASTERR);
+
+                if (!bRet)
+                {
+                    rc = 1;
+                    goto End;
+                }
+
+                PROGRESS_BAR_SET_POS(PT_WRITE_VENTOY_START + i);
+            }
+        }
+        else
+        {
+            rc = 1;
+            Log("decompress finished failed");
+            goto End;
+        }
+    }
+
+End:
+
+    if (data) free(data);
+
+    if (partwrite)
+    {
+        for (i = 0; i < VENTOY_EFI_PART_SIZE / SIZE_1MB; i++)
+        {
+            if (g_part_img_buf[i]) free(g_part_img_buf[i]);
+        }
+    }
+    else
+    {
+        if (g_part_img_buf[0]) free(g_part_img_buf[0]);
+    }
+
+    return rc;
+}
+
+static int WriteGrubStage1ToPhyDrive(HANDLE hDrive)
+{
+    int Len = 0;
+    int readLen = 0;
+    BOOL bRet;
+    DWORD dwSize;
+    BYTE *ImgBuf = NULL;
+    BYTE *RawBuf = NULL;
+
+    Log("WriteGrubStage1ToPhyDrive ...");
+
+    RawBuf = (BYTE *)malloc(SIZE_1MB);
+    if (!RawBuf)
+    {
+        return 1;
+    }
+
+    if (ReadWholeFileToBuf(VENTOY_FILE_STG1_IMG, 0, (void **)&ImgBuf, &Len))
+    {
+        Log("Failed to read stage1 img");
+        free(RawBuf);
+        return 1;
+    }
+
+    unxz(ImgBuf, Len, NULL, NULL, RawBuf, &readLen, unxz_error);
+
+    SetFilePointer(hDrive, 512, NULL, FILE_BEGIN);
+
+    bRet = WriteFile(hDrive, RawBuf, SIZE_1MB - 512, &dwSize, NULL);
+    Log("WriteFile Ret:%u dwSize:%u ErrCode:%u", bRet, dwSize, GetLastError());
+
+    free(RawBuf);
+    free(ImgBuf);
+    return 0;
+}
+
+
+
+static int FormatPart1exFAT(UINT64 DiskSizeBytes)
+{
+    MKFS_PARM Option;
+    FRESULT Ret;
+    FATFS fs;
+
+    Option.fmt = FM_EXFAT;
+    Option.n_fat = 1;
+    Option.align = 8;
+    Option.n_root = 1;
+
+    // < 32GB select 32KB as cluster size
+    // > 32GB select 128KB as cluster size
+    if (DiskSizeBytes / 1024 / 1024 / 1024 <= 32)
+    {
+        Option.au_size = 32768;
+    }
+    else
+    {
+        Option.au_size = 131072;
+    }
+
+    Log("Formatting Part1 exFAT ...");
+
+    Ret = f_mkfs(TEXT("0:"), &Option, 0, 8 * 1024 * 1024);
+
+    if (FR_OK == Ret)
+    {
+        Log("Formatting Part1 exFAT success");
+
+        Ret = f_mount(&fs, TEXT("0:"), 1);
+        Log("mount part %d", Ret);
+
+        if (FR_OK == Ret)
+        {
+            Ret = f_setlabel(TEXT("Ventoy"));
+            Log("f_setlabel %d", Ret);
+
+            Ret = f_mount(0, TEXT("0:"), 1);
+            Log("umount part %d", Ret);
+        }
+
+        return 0;
+    }
+    else
+    {
+        Log("Formatting Part1 exFAT failed");
+        return 1;
+    }
+}
+
+
+int InstallVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive)
+{
+    int i;
+    int rc = 0;
+    int state = 0;
+    HANDLE hDrive;
+    DWORD dwSize;
+    BOOL bRet;
+    CHAR MountDrive;
+    CHAR DriveName[] = "?:\\";
+    CHAR DriveLetters[MAX_PATH] = { 0 };
+    MBR_HEAD MBR;
+
+    Log("InstallVentoy2PhyDrive PhyDrive%d <<%s %s %dGB>>",
+        pPhyDrive->PhyDrive, pPhyDrive->VendorId, pPhyDrive->ProductId,
+        GetHumanReadableGBSize(pPhyDrive->SizeInBytes));
+
+    PROGRESS_BAR_SET_POS(PT_LOCK_FOR_CLEAN);
+
+    VentoyFillMBR(pPhyDrive->SizeInBytes, &MBR);
+
+    Log("Lock disk for clean ............................. ");
+
+    hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, FALSE, FALSE);
+    if (hDrive == INVALID_HANDLE_VALUE)
+    {
+        Log("Failed to open physical disk");
+        return 1;
+    }
+
+    GetLettersBelongPhyDrive(pPhyDrive->PhyDrive, DriveLetters, sizeof(DriveLetters));
+
+    if (DriveLetters[0] == 0)
+    {
+        Log("No drive letter was assigned...");
+        DriveName[0] = GetFirstUnusedDriveLetter();
+        Log("GetFirstUnusedDriveLetter %C: ...", DriveName[0]);
+    }
+    else
+    {
+        // Unmount all mounted volumes that belong to this drive
+        // Do it in reverse so that we always end on the first volume letter
+        for (i = (int)strlen(DriveLetters); i > 0; i--)
+        {
+            DriveName[0] = DriveLetters[i - 1];
+            bRet = DeleteVolumeMountPointA(DriveName);
+            Log("Delete mountpoint %s ret:%u code:%u", DriveName, bRet, GetLastError());
+        }
+    }
+
+    MountDrive = DriveName[0];
+    Log("Will use '%C:' as volume mountpoint", DriveName[0]);
+
+    // It kind of blows, but we have to relinquish access to the physical drive
+    // for VDS to be able to delete the partitions that reside on it...
+    DeviceIoControl(hDrive, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL);
+    CHECK_CLOSE_HANDLE(hDrive);
+
+    PROGRESS_BAR_SET_POS(PT_DEL_ALL_PART);
+
+    if (!DeletePartitions(pPhyDrive->PhyDrive, FALSE))
+    {
+        Log("Notice: Could not delete partitions: %u", GetLastError());
+    }
+
+    Log("Deleting all partitions ......................... OK");
+
+    PROGRESS_BAR_SET_POS(PT_LOCK_FOR_WRITE);
+
+    Log("Lock disk for write ............................. ");
+    hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, TRUE, FALSE);
+    if (hDrive == INVALID_HANDLE_VALUE)
+    {
+        Log("Failed to GetPhysicalHandle for write.");
+        rc = 1;
+        goto End;
+    }
+
+    //Refresh Drive Layout
+    DeviceIoControl(hDrive, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &dwSize, NULL);
+
+    disk_io_set_param(hDrive, MBR.PartTbl[0].StartSectorId + MBR.PartTbl[0].SectorCount);
+
+    PROGRESS_BAR_SET_POS(PT_FORMAT_PART1);
+
+    Log("Formatting part1 exFAT ...");
+    if (0 != FormatPart1exFAT(pPhyDrive->SizeInBytes))
+    {
+        rc = 1;
+        goto End;
+    }
+
+    PROGRESS_BAR_SET_POS(PT_FORMAT_PART2);
+    Log("Writing part2 FAT img ...");
+    if (0 != FormatPart2Fat(hDrive, MBR.PartTbl[1].StartSectorId))
+    {
+        rc = 1;
+        goto End;
+    }
+
+    PROGRESS_BAR_SET_POS(PT_WRITE_STG1_IMG);
+    Log("Writting Boot Image ............................. ");
+    if (WriteGrubStage1ToPhyDrive(hDrive) != 0)
+    {
+        rc = 1;
+        goto End;
+    }
+
+
+    PROGRESS_BAR_SET_POS(PT_WRITE_PART_TABLE);
+    Log("Writting Partition Table ........................ ");
+    SetFilePointer(hDrive, 0, NULL, FILE_BEGIN);
+    if (!WriteFile(hDrive, &MBR, sizeof(MBR), &dwSize, NULL))
+    {
+        rc = 1;
+        Log("Write MBR Failed, dwSize:%u ErrCode:%u", dwSize, GetLastError());
+        goto End;
+    }
+
+    Log("Write MBR OK ...");
+
+    //Refresh Drive Layout
+    DeviceIoControl(hDrive, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &dwSize, NULL);
+
+End:
+    CHECK_CLOSE_HANDLE(hDrive);
+
+    PROGRESS_BAR_SET_POS(PT_MOUNT_VOLUME);
+    Log("Mounting Ventoy Partition ....................... ");
+    Sleep(1000);
+
+    state = 0;
+    memset(DriveLetters, 0, sizeof(DriveLetters));
+    GetLettersBelongPhyDrive(pPhyDrive->PhyDrive, DriveLetters, sizeof(DriveLetters));
+    Log("Logical drive letter after write ventoy: <%s>", DriveLetters);
+
+    for (i = 0; i < sizeof(DriveLetters) && DriveLetters[i]; i++)
+    {
+        DriveName[0] = DriveLetters[i];
+        if (IsVentoyLogicalDrive(DriveName[0]))
+        {
+            Log("%s is ventoy part2, delete mountpoint", DriveName);
+            DeleteVolumeMountPointA(DriveName);
+        }
+        else
+        {
+            Log("%s is ventoy part1, already mounted", DriveName);
+            state = 1;
+        }
+    }
+
+    if (state != 1)
+    {
+        Log("need to mount ventoy part1...");
+        if (0 == GetVentoyVolumeName(pPhyDrive->PhyDrive, MBR.PartTbl[0].StartSectorId, DriveLetters, sizeof(DriveLetters), FALSE))
+        {
+            DriveName[0] = MountDrive;
+            bRet = SetVolumeMountPointA(DriveName, DriveLetters);
+            Log("SetVolumeMountPoint <%s> <%s> bRet:%u code:%u", DriveName, DriveLetters, bRet, GetLastError());
+        }
+        else
+        {
+            Log("Failed to find ventoy volume");
+        }
+    }
+
+    Log("OK\n");
+
+    return rc;
+}
+
+int UpdateVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive)
+{
+    int i;
+    int rc = 0;
+    BOOL ForceMBR = FALSE;
+    HANDLE hVolume;
+    HANDLE hDrive;
+    DWORD Status;
+    DWORD dwSize;
+    BOOL bRet;
+    CHAR DriveName[] = "?:\\";
+    CHAR DriveLetters[MAX_PATH] = { 0 };
+    UINT32 StartSector;
+    MBR_HEAD BootImg;
+    MBR_HEAD MBR;
+
+    StartSector = (UINT32)(pPhyDrive->SizeInBytes / 512 - VENTOY_EFI_PART_SIZE / 512);
+
+    Log("UpdateVentoy2PhyDrive PhyDrive%d <<%s %s %dGB>>",
+        pPhyDrive->PhyDrive, pPhyDrive->VendorId, pPhyDrive->ProductId, 
+        GetHumanReadableGBSize(pPhyDrive->SizeInBytes));
+
+    PROGRESS_BAR_SET_POS(PT_LOCK_FOR_CLEAN);
+
+    Log("Lock disk for umount ............................ ");
+
+    hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, FALSE, FALSE);
+    if (hDrive == INVALID_HANDLE_VALUE)
+    {
+        Log("Failed to open physical disk");
+        return 1;
+    }
+
+    // Read MBR
+    ReadFile(hDrive, &MBR, sizeof(MBR), &dwSize, NULL);
+
+    GetLettersBelongPhyDrive(pPhyDrive->PhyDrive, DriveLetters, sizeof(DriveLetters));
+
+    if (DriveLetters[0] == 0)
+    {
+        Log("No drive letter was assigned...");
+    }
+    else
+    {
+        // Unmount all mounted volumes that belong to this drive
+        // Do it in reverse so that we always end on the first volume letter
+        for (i = (int)strlen(DriveLetters); i > 0; i--)
+        {
+            DriveName[0] = DriveLetters[i - 1];
+            if (IsVentoyLogicalDrive(DriveName[0]))
+            {
+                Log("%s is ventoy logical drive", DriveName);
+                bRet = DeleteVolumeMountPointA(DriveName);
+                Log("Delete mountpoint %s ret:%u code:%u", DriveName, bRet, LASTERR);
+                break;
+            }
+        }
+    }
+
+    // It kind of blows, but we have to relinquish access to the physical drive
+    // for VDS to be able to delete the partitions that reside on it...
+    DeviceIoControl(hDrive, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL);
+    CHECK_CLOSE_HANDLE(hDrive);
+
+    PROGRESS_BAR_SET_POS(PT_LOCK_FOR_WRITE);
+
+    Log("Lock disk for update ............................ ");
+    hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, TRUE, FALSE);
+    if (hDrive == INVALID_HANDLE_VALUE)
+    {
+        Log("Failed to GetPhysicalHandle for write.");
+        rc = 1;
+        goto End;
+    }
+
+    PROGRESS_BAR_SET_POS(PT_LOCK_VOLUME);
+
+    Log("Lock volume for update .......................... ");
+    hVolume = INVALID_HANDLE_VALUE;
+    Status = GetVentoyVolumeName(pPhyDrive->PhyDrive, MBR.PartTbl[1].StartSectorId, DriveLetters, sizeof(DriveLetters), TRUE);
+    if (ERROR_SUCCESS == Status)
+    {
+        Log("Now lock and dismount volume <%s>", DriveLetters);
+        hVolume = CreateFileA(DriveLetters,
+            GENERIC_READ | GENERIC_WRITE,
+            FILE_SHARE_READ,
+            NULL,
+            OPEN_EXISTING,
+            FILE_ATTRIBUTE_NORMAL | FILE_FLAG_NO_BUFFERING | FILE_FLAG_WRITE_THROUGH,
+            NULL);
+
+        if (hVolume == INVALID_HANDLE_VALUE)
+        {
+            Log("Failed to create file volume, errcode:%u", LASTERR);
+            rc = 1;
+            goto End;
+        }
+
+        bRet = DeviceIoControl(hVolume, FSCTL_LOCK_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL);
+        Log("FSCTL_LOCK_VOLUME bRet:%u code:%u", bRet, LASTERR);
+
+        bRet = DeviceIoControl(hVolume, FSCTL_DISMOUNT_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL);
+        Log("FSCTL_DISMOUNT_VOLUME bRet:%u code:%u", bRet, LASTERR);
+    }
+    else if (ERROR_NOT_FOUND == Status)
+    {
+        Log("Volume not found, maybe not supported");
+    }
+    else
+    {
+        rc = 1;
+        goto End;
+    }
+
+
+    if (!TryWritePart2(hDrive, StartSector))
+    {
+        ForceMBR = TRUE;
+        Log("Try write failed, now delete partition 2...");
+
+        CHECK_CLOSE_HANDLE(hDrive);
+
+        Log("Now delete partition 2...");
+        DeletePartitions(pPhyDrive->PhyDrive, TRUE);
+
+        hDrive = GetPhysicalHandle(pPhyDrive->PhyDrive, TRUE, TRUE, FALSE);
+        if (hDrive == INVALID_HANDLE_VALUE)
+        {
+            Log("Failed to GetPhysicalHandle for write.");
+            rc = 1;
+            goto End;
+        }
+    }
+
+
+    PROGRESS_BAR_SET_POS(PT_FORMAT_PART2);
+
+    Log("Write Ventoy to disk ............................ ");
+    if (0 != FormatPart2Fat(hDrive, StartSector))
+    {
+        rc = 1;
+        goto End;
+    }
+
+    if (hVolume != INVALID_HANDLE_VALUE)
+    {
+        bRet = DeviceIoControl(hVolume, FSCTL_UNLOCK_VOLUME, NULL, 0, NULL, 0, &dwSize, NULL);
+        Log("FSCTL_UNLOCK_VOLUME bRet:%u code:%u", bRet, LASTERR);
+        CHECK_CLOSE_HANDLE(hVolume);
+    }
+
+    Log("Updating Boot Image ............................. ");
+    if (WriteGrubStage1ToPhyDrive(hDrive) != 0)
+    {
+        rc = 1;
+        goto End;
+    }
+
+    // Boot Image
+    VentoyGetLocalBootImg(&BootImg);
+
+    // Use Old UUID
+    memcpy(BootImg.BootCode + 0x180, MBR.BootCode + 0x180, 16);
+
+    if (ForceMBR == FALSE && memcmp(BootImg.BootCode, MBR.BootCode, 440) == 0)
+    {
+        Log("Boot image has no difference, no need to write.");
+    }
+    else
+    {
+        Log("Boot image need to write %u.", ForceMBR);
+
+        SetFilePointer(hDrive, 0, NULL, FILE_BEGIN);
+
+        memcpy(MBR.BootCode, BootImg.BootCode, 440);
+        bRet = WriteFile(hDrive, &MBR, 512, &dwSize, NULL);
+        Log("Write Boot Image ret:%u dwSize:%u Error:%u", bRet, dwSize, LASTERR);
+    }
+
+    //Refresh Drive Layout
+    DeviceIoControl(hDrive, IOCTL_DISK_UPDATE_PROPERTIES, NULL, 0, NULL, 0, &dwSize, NULL);
+
+End:
+    CHECK_CLOSE_HANDLE(hDrive);
+
+    return rc;
+}
+
diff --git a/Ventoy2Disk/Ventoy2Disk/Res/Ventoy2Disk.manifest b/Ventoy2Disk/Ventoy2Disk/Res/Ventoy2Disk.manifest
new file mode 100644 (file)
index 0000000..8d2a6e0
--- /dev/null
@@ -0,0 +1,38 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+       <dependency> 
+    <dependentAssembly> 
+        <assemblyIdentity 
+            type="win32" 
+            name="Microsoft.Windows.Common-Controls" 
+            version="6.0.0.0" 
+            processorArchitecture="x86" 
+            publicKeyToken="6595b64144ccf1df" 
+            language="*" 
+        /> 
+    </dependentAssembly> 
+       </dependency> 
+    
+    <compatibility xmlns="urn:schemas-microsoft-com:compatibility.v1">
+        <application>
+            <!-- Windows 10 -->
+            <supportedOS Id="{8e0f7a12-bfb3-4fe8-b9a5-48fd50a15a9a}"/>
+            <!-- Windows 8.1 -->
+            <supportedOS Id="{1f676c76-80e1-4239-95bb-83d0f6d0da78}"/>
+            <!-- Windows 8 -->
+            <supportedOS Id="{4a2f28e3-53b9-4441-ba9c-d69d4a4a6e38}"/>
+            <!-- Windows 7 -->
+            <supportedOS Id="{35138b9a-5d96-4fbd-8e2d-a2440225f93a}"/>
+            <!-- Windows Vista -->
+            <supportedOS Id="{e2011457-1546-43c5-a5fe-008deee3d3f0}"/> 
+        </application>
+    </compatibility>
+    
+       <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+    <security>
+      <requestedPrivileges>
+        <requestedExecutionLevel level="requireAdministrator" uiAccess="false"></requestedExecutionLevel>
+      </requestedPrivileges>
+    </security>
+  </trustInfo>
+</assembly>
\ No newline at end of file
diff --git a/Ventoy2Disk/Ventoy2Disk/Res/ventoy.ico b/Ventoy2Disk/Ventoy2Disk/Res/ventoy.ico
new file mode 100644 (file)
index 0000000..c069f8e
Binary files /dev/null and b/Ventoy2Disk/Ventoy2Disk/Res/ventoy.ico differ
diff --git a/Ventoy2Disk/Ventoy2Disk/Utility.c b/Ventoy2Disk/Ventoy2Disk/Utility.c
new file mode 100644 (file)
index 0000000..f3357d8
--- /dev/null
@@ -0,0 +1,635 @@
+/******************************************************************************
+ * Utility.c
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include <Windows.h>
+#include "Ventoy2Disk.h"
+
+void Log(const char *Fmt, ...)
+{
+    va_list Arg;
+    int Len = 0;
+    FILE *File = NULL;
+    SYSTEMTIME Sys;
+    char szBuf[1024];
+
+    GetLocalTime(&Sys);
+    Len += safe_sprintf(szBuf,
+        "[%4d/%02d/%02d %02d:%02d:%02d.%03d] ",
+        Sys.wYear, Sys.wMonth, Sys.wDay,
+        Sys.wHour, Sys.wMinute, Sys.wSecond,
+        Sys.wMilliseconds);
+
+    va_start(Arg, Fmt);
+    Len += vsnprintf_s(szBuf + Len, sizeof(szBuf)-Len, sizeof(szBuf)-Len, Fmt, Arg);
+    va_end(Arg);
+
+    //printf("%s\n", szBuf);
+
+#if 1
+    fopen_s(&File, VENTOY_FILE_LOG, "a+");
+    if (File)
+    {
+        fwrite(szBuf, 1, Len, File);
+        fwrite("\n", 1, 1, File);
+        fclose(File);
+    }
+#endif
+
+}
+
+BOOL IsPathExist(BOOL Dir, const char *Fmt, ...)
+{
+    va_list Arg;
+    HANDLE hFile;
+    DWORD Attr;
+    CHAR FilePath[MAX_PATH];
+
+    va_start(Arg, Fmt);
+    vsnprintf_s(FilePath, sizeof(FilePath), sizeof(FilePath), Fmt, Arg);
+    va_end(Arg);
+
+    hFile = CreateFileA(FilePath, FILE_READ_EA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
+    if (INVALID_HANDLE_VALUE == hFile)
+    {
+        return FALSE;
+    }
+
+    CloseHandle(hFile);
+
+    Attr = GetFileAttributesA(FilePath);
+
+    if (Dir)
+    {
+        if ((Attr & FILE_ATTRIBUTE_DIRECTORY) == 0)
+        {
+            return FALSE;
+        }
+    }
+    else
+    {
+        if (Attr & FILE_ATTRIBUTE_DIRECTORY)
+        {
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+
+int ReadWholeFileToBuf(const CHAR *FileName, int ExtLen, void **Bufer, int *BufLen)
+{
+    int FileSize;
+    FILE *File = NULL;
+    void *Data = NULL;
+
+    fopen_s(&File, FileName, "rb");
+    if (File == NULL)
+    {
+        Log("Failed to open file %s", FileName);
+        return 1;
+    }
+
+    fseek(File, 0, SEEK_END);
+    FileSize = (int)ftell(File);
+
+    Data = malloc(FileSize + ExtLen);
+    if (!Data)
+    {
+        fclose(File);
+        return 1;
+    }
+
+    fseek(File, 0, SEEK_SET);
+    fread(Data, 1, FileSize, File);
+
+    fclose(File);
+
+    *Bufer = Data;
+    *BufLen = FileSize;
+
+    return 0;
+}
+
+const CHAR* GetLocalVentoyVersion(void)
+{
+    int rc;
+    int FileSize;
+    CHAR *Pos = NULL;
+    CHAR *Buf = NULL;
+    static CHAR LocalVersion[64] = { 0 };
+
+    if (LocalVersion[0] == 0)
+    {
+        rc = ReadWholeFileToBuf(VENTOY_FILE_VERSION, 1, (void **)&Buf, &FileSize);
+        if (rc)
+        {
+            return "";
+        }
+        Buf[FileSize] = 0;
+
+        for (Pos = Buf; *Pos; Pos++)
+        {
+            if (*Pos == '\r' || *Pos == '\n')
+            {
+                *Pos = 0;
+                break;
+            }
+        }
+
+        safe_sprintf(LocalVersion, "%s", Buf);
+        free(Buf);
+    }
+    
+    return LocalVersion;
+}
+
+const CHAR* ParseVentoyVersionFromString(CHAR *Buf)
+{
+    CHAR *Pos = NULL;
+    CHAR *End = NULL;
+    static CHAR LocalVersion[64] = { 0 };
+
+    Pos = strstr(Buf, "VENTOY_VERSION=");
+    if (Pos)
+    {
+        Pos += strlen("VENTOY_VERSION=");
+        if (*Pos == '"')
+        {
+            Pos++;
+        }
+
+        End = Pos;
+        while (*End != 0 && *End != '"' && *End != '\r' && *End != '\n')
+        {
+            End++;
+        }
+
+        *End = 0;
+
+        safe_sprintf(LocalVersion, "%s", Pos);
+        return LocalVersion;
+    }
+
+    return "";
+}
+
+BOOL IsWow64(void)
+{
+    typedef BOOL(WINAPI *LPFN_ISWOW64PROCESS)(HANDLE, PBOOL);
+    LPFN_ISWOW64PROCESS fnIsWow64Process;
+    BOOL bIsWow64 = FALSE;
+
+    fnIsWow64Process = (LPFN_ISWOW64PROCESS)GetProcAddress(GetModuleHandleA("kernel32"), "IsWow64Process");
+    if (NULL != fnIsWow64Process)
+    {
+        fnIsWow64Process(GetCurrentProcess(), &bIsWow64);
+    }
+
+    return bIsWow64;
+}
+
+void DumpWindowsVersion(void)
+{
+    int Bit; 
+    BOOL WsVer;    
+    DWORD Major, Minor;
+    ULONGLONG MajorEqual, MinorEqual;
+    OSVERSIONINFOEXA Ver1, Ver2;
+    const CHAR *Ver = NULL; 
+    CHAR WinVer[256] = { 0 };
+
+    memset(&Ver1, 0, sizeof(Ver1));
+    memset(&Ver2, 0, sizeof(Ver2));
+
+    Ver1.dwOSVersionInfoSize = sizeof(Ver1);
+    
+    // suppress the C4996 warning for GetVersionExA
+    #pragma warning(push)
+    #pragma warning(disable:4996)
+    if (!GetVersionExA((OSVERSIONINFOA *)&Ver1))
+    {
+        memset(&Ver1, 0, sizeof(Ver1));
+        Ver1.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
+        if (!GetVersionExA((OSVERSIONINFOA *)&Ver1))
+        {
+            return;
+        }
+    }
+    #pragma warning(pop)
+
+    if (Ver1.dwPlatformId == VER_PLATFORM_WIN32_NT)
+    {
+        if (Ver1.dwMajorVersion > 6 || (Ver1.dwMajorVersion == 6 && Ver1.dwMinorVersion >= 2))
+        {
+            // GetVersionEx() has problem on some Windows version 
+
+            MajorEqual = VerSetConditionMask(0, VER_MAJORVERSION, VER_EQUAL);
+            for (Major = Ver1.dwMajorVersion; Major <= 9; Major++) 
+            {
+                memset(&Ver2, 0, sizeof(Ver2));
+                Ver2.dwOSVersionInfoSize = sizeof(Ver2);
+                Ver2.dwMajorVersion = Major;
+
+                if (!VerifyVersionInfoA(&Ver2, VER_MAJORVERSION, MajorEqual))
+                {
+                    continue;
+                }
+                    
+                if (Ver1.dwMajorVersion < Major) 
+                {
+                    Ver1.dwMajorVersion = Major;
+                    Ver1.dwMinorVersion = 0;
+                }
+
+                MinorEqual = VerSetConditionMask(0, VER_MINORVERSION, VER_EQUAL);
+                for (Minor = Ver1.dwMinorVersion; Minor <= 9; Minor++) 
+                {
+                    memset(&Ver2, 0, sizeof(Ver2)); 
+                    
+                    Ver2.dwOSVersionInfoSize = sizeof(Ver2);
+                    Ver2.dwMinorVersion = Minor;
+
+                    if (!VerifyVersionInfoA(&Ver2, VER_MINORVERSION, MinorEqual))
+                    {
+                        continue;
+                    }
+                        
+                    Ver1.dwMinorVersion = Minor;
+                    break;
+                }
+
+                break;
+            }
+        }
+
+        if (Ver1.dwMajorVersion <= 0xF && Ver1.dwMinorVersion <= 0xF)
+        {
+            WsVer = (Ver1.wProductType <= VER_NT_WORKSTATION);
+            switch ((Ver1.dwMajorVersion << 4) | Ver2.dwMinorVersion)
+            {
+                case 0x51:
+                {
+                    Ver = "XP";
+                    break;
+                }
+                case 0x52:
+                {
+                    Ver = GetSystemMetrics(89) ? "Server 2003 R2" : "Server 2003";
+                    break;
+                }
+                case 0x60:
+                {
+                    Ver = WsVer ? "Vista" : "Server 2008";
+                    break;
+                }
+                case 0x61:
+                {
+                    Ver = WsVer ? "7" : "Server 2008 R2";
+                    break;
+                }
+                case 0x62:
+                {
+                    Ver = WsVer ? "8" : "Server 2012";
+                    break;
+                }
+                case 0x63:
+                {
+                    Ver = WsVer ? "8.1" : "Server 2012 R2";
+                    break;
+                }
+                case 0x64:
+                {
+                    Ver = WsVer ? "10 (Preview 1)" : "Server 10 (Preview 1)";
+                    break;
+                }
+                case 0xA0:
+                {
+                    Ver = WsVer ? "10" : ((Ver1.dwBuildNumber > 15000) ? "Server 2019" : "Server 2016");
+                    break;
+                }
+                default:
+                {
+                    Ver = "10 or later";
+                    break;
+                }
+            }
+        }
+    }
+
+    Bit = IsWow64() ? 64 : 32;
+
+    if (Ver1.wServicePackMinor)
+    {
+        safe_sprintf(WinVer, "Windows %s SP%u.%u %d-bit", Ver, Ver1.wServicePackMajor, Ver1.wServicePackMinor, Bit);
+    }
+    else if (Ver1.wServicePackMajor)
+    {
+        safe_sprintf(WinVer, "Windows %s SP%u %d-bit", Ver, Ver1.wServicePackMajor, Bit);
+    }
+    else
+    {
+        safe_sprintf(WinVer, "Windows %s %d-bit", Ver, Bit);
+    }
+
+    if (((Ver1.dwMajorVersion << 4) | Ver2.dwMinorVersion) >= 0x62)
+    {
+        Log("Windows Version : %s (Build %u)", WinVer, Ver1.dwBuildNumber);
+    }
+    else
+    {
+        Log("Windows Version : %s", WinVer);
+    }
+
+    return;
+}
+
+BOOL IsVentoyLogicalDrive(CHAR DriveLetter)
+{
+    int i;
+    CONST CHAR *Files[] =
+    {
+        "EFI\\BOOT\\BOOTX64.EFI",
+        "grub\\themes\\ventoy\\theme.txt",
+        "ventoy\\ventoy.cpio",
+    };
+
+    for (i = 0; i < sizeof(Files) / sizeof(Files[0]); i++)
+    {
+        if (!IsFileExist("%C:\\%s", DriveLetter, Files[i]))
+        {
+            return FALSE;
+        }
+    }
+
+    return TRUE;
+}
+
+
+static int VentoyFillLocation(UINT64 DiskSizeInBytes, UINT32 StartSectorId, UINT32 SectorCount, PART_TABLE *Table)
+{
+    BYTE Head;
+    BYTE Sector;
+    BYTE nSector = 63;
+    BYTE nHead = 8;    
+    UINT32 Cylinder;
+    UINT32 EndSectorId;
+
+    while (nHead != 0 && (DiskSizeInBytes / 512 / nSector / nHead) > 1024)
+    {
+        nHead = (BYTE)nHead * 2;
+    }
+
+    if (nHead == 0)
+    {
+        nHead = 255;
+    }
+
+    Cylinder = StartSectorId / nSector / nHead;
+    Head = StartSectorId / nSector % nHead;
+    Sector = StartSectorId % nSector + 1;
+
+    Table->StartHead = Head;
+    Table->StartSector = Sector;
+    Table->StartCylinder = Cylinder;
+
+    EndSectorId = StartSectorId + SectorCount - 1;
+    Cylinder = EndSectorId / nSector / nHead;
+    Head = EndSectorId / nSector % nHead;
+    Sector = EndSectorId % nSector + 1;
+
+    Table->EndHead = Head;
+    Table->EndSector = Sector;
+    Table->EndCylinder = Cylinder;
+
+    Table->StartSectorId = StartSectorId;
+    Table->SectorCount = SectorCount;
+
+    return 0;
+}
+
+int VentoyFillMBR(UINT64 DiskSizeBytes, MBR_HEAD *pMBR)
+{
+    UINT32 DiskSectorCount;
+    UINT32 PartSectorCount;
+    UINT32 PartStartSector;
+
+    VentoyGetLocalBootImg(pMBR);
+
+    DiskSectorCount = (UINT32)(DiskSizeBytes / 512);
+
+    //Part1
+    PartStartSector = VENTOY_PART1_START_SECTOR;
+    PartSectorCount = DiskSectorCount - VENTOY_EFI_PART_SIZE / 512 - PartStartSector;
+    VentoyFillLocation(DiskSizeBytes, PartStartSector, PartSectorCount, pMBR->PartTbl);
+
+    pMBR->PartTbl[0].Active = 0x00;
+    pMBR->PartTbl[0].FsFlag = 0x07; // exFAT/NTFS/HPFS
+
+    //Part2
+    PartStartSector += PartSectorCount;
+    PartSectorCount = VENTOY_EFI_PART_SIZE / 512;
+    VentoyFillLocation(DiskSizeBytes, PartStartSector, PartSectorCount, pMBR->PartTbl + 1);
+
+    pMBR->PartTbl[1].Active = 0x80; // bootable
+    pMBR->PartTbl[1].FsFlag = 0xEF; // EFI System Partition
+
+    pMBR->Byte55 = 0x55;
+    pMBR->ByteAA = 0xAA;
+
+    return 0;
+}
+
+CHAR GetFirstUnusedDriveLetter(void)
+{
+    CHAR Letter = 'D';
+    DWORD Drives = GetLogicalDrives();
+
+    Drives >>= 3;
+    while (Drives & 0x1)
+    {
+        Letter++;
+        Drives >>= 1;
+    }
+
+    return Letter;
+}
+
+const CHAR * GetBusTypeString(STORAGE_BUS_TYPE Type)
+{
+    switch (Type)
+    {
+        case BusTypeUnknown: return "unknown";
+        case BusTypeScsi: return "SCSI";
+        case BusTypeAtapi: return "Atapi";
+        case BusTypeAta: return "ATA";
+        case BusType1394: return "1394";
+        case BusTypeSsa: return "SSA";
+        case BusTypeFibre: return "Fibre";
+        case BusTypeUsb: return "USB";
+        case BusTypeRAID: return "RAID";
+        case BusTypeiScsi: return "iSCSI";
+        case BusTypeSas: return "SAS";
+        case BusTypeSata: return "SATA";
+        case BusTypeSd: return "SD";
+        case BusTypeMmc: return "MMC";
+        case BusTypeVirtual: return "Virtual";
+        case BusTypeFileBackedVirtual: return "FileBackedVirtual";
+        case BusTypeSpaces: return "Spaces";
+        case BusTypeNvme: return "Nvme";
+    }
+    return "unknown";
+}
+
+int VentoyGetLocalBootImg(MBR_HEAD *pMBR)
+{
+    int Len = 0;
+    BYTE *ImgBuf = NULL;
+    static int Loaded = 0;
+    static MBR_HEAD MBR;
+
+    if (Loaded)
+    {
+        memcpy(pMBR, &MBR, 512);
+        return 0;
+    }
+
+    if (0 == ReadWholeFileToBuf(VENTOY_FILE_BOOT_IMG, 0, (void **)&ImgBuf, &Len))
+    {
+        Log("Copy boot img success");
+        memcpy(pMBR, ImgBuf, 512);
+        free(ImgBuf);
+        
+        CoCreateGuid((GUID *)(pMBR->BootCode + 0x180));
+
+        memcpy(&MBR, pMBR, 512);
+        Loaded = 1;
+
+        return 0;
+    }
+    else
+    {
+        Log("Copy boot img failed");
+        return 1;
+    }
+}
+
+int GetHumanReadableGBSize(UINT64 SizeBytes)
+{
+    int i;
+    int Pow2 = 1;
+    double Delta;
+    double GB = SizeBytes * 1.0 / 1000 / 1000 / 1000;
+
+    for (i = 0; i < 12; i++)
+    {
+        if (Pow2 > GB)
+        {
+            Delta = (Pow2 - GB) / Pow2;
+        }
+        else
+        {
+            Delta = (GB - Pow2) / Pow2;
+        }
+
+        if (Delta < 0.05)
+        {
+            return Pow2;
+        }
+
+        Pow2 <<= 1;
+    }
+
+    return (int)GB;
+}
+
+void TrimString(CHAR *String)
+{
+    CHAR *Pos1 = String;
+    CHAR *Pos2 = String;
+    size_t Len = strlen(String);
+
+    while (Len > 0)
+    {
+        if (String[Len - 1] != ' ' && String[Len - 1] != '\t')
+        {
+            break;
+        }
+        String[Len - 1] = 0;
+        Len--;
+    }
+
+    while (*Pos1 == ' ' || *Pos1 == '\t')
+    {
+        Pos1++;
+    }
+
+    while (*Pos1)
+    {
+        *Pos2++ = *Pos1++;
+    }
+    *Pos2++ = 0;
+
+    return;
+}
+
+int GetRegDwordValue(HKEY Key, LPCSTR SubKey, LPCSTR ValueName, DWORD *pValue)
+{
+    HKEY hKey;
+    DWORD Type;
+    DWORD Size;
+    LSTATUS lRet;
+    DWORD Value;
+
+    lRet = RegOpenKeyExA(Key, SubKey, 0, KEY_QUERY_VALUE, &hKey);
+    Log("RegOpenKeyExA <%s> Ret:%ld", SubKey, lRet);
+
+    if (ERROR_SUCCESS == lRet)
+    {
+        Size = sizeof(Value);
+        lRet = RegQueryValueExA(hKey, ValueName, NULL, &Type, (LPBYTE)&Value, &Size);
+        Log("RegQueryValueExA <%s> ret:%u  Size:%u Value:%u", ValueName, lRet, Size, Value);
+
+        *pValue = Value;
+        RegCloseKey(hKey);
+
+        return 0;
+    }
+    else
+    {
+        return 1;
+    }
+}
+
+int GetPhysicalDriveCount(void)
+{
+    DWORD Value;
+    int Count = 0;
+
+    if (GetRegDwordValue(HKEY_LOCAL_MACHINE, "SYSTEM\\CurrentControlSet\\Services\\disk\\Enum", "Count", &Value) == 0)
+    {
+        Count = (int)Value;
+    }
+
+    Log("GetPhysicalDriveCount: %d", Count);
+    return Count;
+}
+
+
+
diff --git a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.aps b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.aps
new file mode 100644 (file)
index 0000000..3687cc0
Binary files /dev/null and b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.aps differ
diff --git a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.c b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.c
new file mode 100644 (file)
index 0000000..f6f7734
--- /dev/null
@@ -0,0 +1,281 @@
+/******************************************************************************
+ * Ventoy2Disk.c
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <Windows.h>
+#include "resource.h"
+#include "Language.h"
+#include "Ventoy2Disk.h"
+
+PHY_DRIVE_INFO *g_PhyDriveList = NULL;
+DWORD g_PhyDriveCount = 0;
+static int g_FilterRemovable = 1;
+static int g_FilterUSB = 1;
+int g_ForceOperation = 1;
+
+int ParseCmdLineOption(LPSTR lpCmdLine)
+{
+    int i;
+    char cfgfile[MAX_PATH];
+
+    if (lpCmdLine && lpCmdLine[0])
+    {
+        Log("CmdLine:<%s>", lpCmdLine);
+    }
+
+    for (i = 0; i < __argc; i++)
+    {
+        if (strncmp(__argv[i], "-R", 2) == 0)
+        {
+            g_FilterRemovable = 0;
+        }
+        else if (strncmp(__argv[i], "-U", 2) == 0)
+        {
+            g_FilterUSB = 0;
+        }
+        else if (strncmp(__argv[i], "-F", 2) == 0)
+        {
+            g_ForceOperation = 1;
+        }
+    }
+
+    GetCurrentDirectoryA(sizeof(cfgfile), cfgfile);
+    strcat_s(cfgfile, sizeof(cfgfile), "\\Ventoy2Disk.ini");
+
+    if (0 == GetPrivateProfileIntA("Filter", "Removable", 1, cfgfile))
+    {
+        g_FilterRemovable = 0;
+    }
+
+    if (0 == GetPrivateProfileIntA("Filter", "USB", 1, cfgfile))
+    {
+        g_FilterUSB = 0;
+    }
+
+    if (1 == GetPrivateProfileIntA("Operation", "Force", 0, cfgfile))
+    {
+        g_ForceOperation = 1;
+    }
+
+    Log("Control Flag: %d %d %d", g_FilterRemovable, g_FilterUSB, g_ForceOperation);
+
+    return 0;
+}
+
+static BOOL IsVentoyPhyDrive(int PhyDrive, UINT64 SizeBytes)
+{
+    int i;
+    BOOL bRet;
+    DWORD dwSize;
+    HANDLE hDrive;
+    MBR_HEAD MBR;
+    UINT32 PartStartSector;
+    UINT32 PartSectorCount;
+    CHAR PhyDrivePath[128];
+
+    safe_sprintf(PhyDrivePath, "\\\\.\\PhysicalDrive%d", PhyDrive);
+    hDrive = CreateFileA(PhyDrivePath, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
+    Log("Create file Handle:%p %s status:%u", hDrive, PhyDrivePath, LASTERR);
+
+    if (hDrive == INVALID_HANDLE_VALUE)
+    {
+        return FALSE;
+    }
+
+    bRet = ReadFile(hDrive, &MBR, sizeof(MBR), &dwSize, NULL);
+    CHECK_CLOSE_HANDLE(hDrive);
+
+    Log("Read MBR Ret:%u Size:%u code:%u", bRet, dwSize, LASTERR);
+
+    if ((!bRet) || (dwSize != sizeof(MBR)))
+    {
+        return FALSE;
+    }
+
+    if (MBR.Byte55 != 0x55 || MBR.ByteAA != 0xAA)
+    {
+        Log("Byte55 ByteAA not match 0x%x 0x%x", MBR.Byte55, MBR.ByteAA);
+        return FALSE;
+    }
+
+    for (i = 0; i < 4; i++)
+    {
+        Log("=========== Partition Table %d ============", i + 1);
+        Log("PartTbl.Active = 0x%x", MBR.PartTbl[i].Active);
+        Log("PartTbl.FsFlag = 0x%x", MBR.PartTbl[i].FsFlag);
+        Log("PartTbl.StartSectorId = %u", MBR.PartTbl[i].StartSectorId);
+        Log("PartTbl.SectorCount = %u", MBR.PartTbl[i].SectorCount);
+        Log("PartTbl.StartHead = %u", MBR.PartTbl[i].StartHead);
+        Log("PartTbl.StartSector = %u", MBR.PartTbl[i].StartSector);
+        Log("PartTbl.StartCylinder = %u", MBR.PartTbl[i].StartCylinder);
+        Log("PartTbl.EndHead = %u", MBR.PartTbl[i].EndHead);
+        Log("PartTbl.EndSector = %u", MBR.PartTbl[i].EndSector);
+        Log("PartTbl.EndCylinder = %u", MBR.PartTbl[i].EndCylinder);
+    }
+
+    if (MBR.PartTbl[2].SectorCount > 0 || MBR.PartTbl[3].SectorCount > 0)
+    {
+        Log("Part3 Part4 are not empty");
+        return FALSE;
+    }
+
+    PartStartSector = 2048;
+    PartSectorCount = (UINT32)((SizeBytes - VENTOY_EFI_PART_SIZE - SIZE_1MB) / 512);
+
+    if (MBR.PartTbl[0].FsFlag != 0x07 ||
+        MBR.PartTbl[0].StartSectorId != PartStartSector ||
+        MBR.PartTbl[0].SectorCount != PartSectorCount)
+    {
+        Log("Part1 not match %u %u", PartStartSector, PartSectorCount);
+        return FALSE;
+    }
+
+    PartStartSector = (UINT32)((SizeBytes - VENTOY_EFI_PART_SIZE) / 512);
+    PartSectorCount = VENTOY_EFI_PART_SIZE / 512;
+
+    if (MBR.PartTbl[1].Active != 0x80 ||
+        MBR.PartTbl[1].FsFlag != 0xEF ||
+        MBR.PartTbl[1].StartSectorId != PartStartSector ||
+        MBR.PartTbl[1].SectorCount != PartSectorCount)
+    {
+        Log("Part2 not match %u %u", PartStartSector, PartSectorCount);
+        return FALSE;
+    }
+
+    Log("PhysicalDrive%d is ventoy disk", PhyDrive);
+    return TRUE;
+}
+
+
+static int FilterPhysicalDrive(PHY_DRIVE_INFO *pDriveList, DWORD DriveCount)
+{
+    DWORD i; 
+    DWORD LogDrive;
+    int Letter = 'A';
+    int Id = 0;
+    int LetterCount = 0;
+    PHY_DRIVE_INFO *CurDrive;
+    int LogLetter[VENTOY_MAX_PHY_DRIVE];
+    int PhyDriveId[VENTOY_MAX_PHY_DRIVE];
+
+    for (LogDrive = GetLogicalDrives(); LogDrive > 0; LogDrive >>= 1)
+    {
+        if (LogDrive & 0x01)
+        {
+            LogLetter[LetterCount] = Letter;
+            PhyDriveId[LetterCount] = GetPhyDriveByLogicalDrive(Letter);
+
+            Log("Logical Drive:%C  ===> PhyDrive:%d", LogLetter[LetterCount], PhyDriveId[LetterCount]);
+            LetterCount++;
+        }
+        
+        Letter++;
+    }    
+
+    for (i = 0; i < DriveCount; i++)
+    {
+        CurDrive = pDriveList + i;
+
+        CurDrive->Id = -1;
+        CurDrive->FirstDriveLetter = -1;
+
+        // Too big for MBR
+        if (CurDrive->SizeInBytes > 2199023255552ULL)
+        {
+            Log("<%s %s> is filtered for too big for MBR.", CurDrive->VendorId, CurDrive->ProductId);
+            continue;
+        }
+
+        if (g_FilterRemovable && (!CurDrive->RemovableMedia))
+        {
+            Log("<%s %s> is filtered for not removable.", CurDrive->VendorId, CurDrive->ProductId);
+            continue;
+        }
+
+        if (g_FilterUSB && CurDrive->BusType != BusTypeUsb)
+        {
+            Log("<%s %s> is filtered for not USB type.", CurDrive->VendorId, CurDrive->ProductId);
+            continue;
+        }
+        
+        CurDrive->Id = Id++;
+
+        for (Letter = 0; Letter < LetterCount; Letter++)
+        {
+            if (PhyDriveId[Letter] == CurDrive->PhyDrive)
+            {
+                CurDrive->FirstDriveLetter = LogLetter[Letter];
+                break;
+            }
+        }
+
+        if (IsVentoyPhyDrive(CurDrive->PhyDrive, CurDrive->SizeInBytes))
+        {
+            GetVentoyVerInPhyDrive(CurDrive, CurDrive->VentoyVersion, sizeof(CurDrive->VentoyVersion));
+        }
+    }
+
+    // for safe
+    for (i = 0; i < DriveCount; i++)
+    {
+        CurDrive = pDriveList + i;
+        if (CurDrive->Id < 0)
+        {
+            CurDrive->PhyDrive = 0x00FFFFFF;
+        }
+    }
+
+    return Id;
+}
+
+PHY_DRIVE_INFO * GetPhyDriveInfoById(int Id)
+{
+    DWORD i;
+    for (i = 0; i < g_PhyDriveCount; i++)
+    {
+        if (g_PhyDriveList[i].Id >= 0 && g_PhyDriveList[i].Id == Id)
+        {
+            return g_PhyDriveList + i;
+        }
+    }
+
+    return NULL;
+}
+
+int Ventoy2DiskInit(void)
+{
+    g_PhyDriveList = (PHY_DRIVE_INFO *)malloc(sizeof(PHY_DRIVE_INFO)* VENTOY_MAX_PHY_DRIVE);
+    if (NULL == g_PhyDriveList)
+    {
+        Log("Failed to alloc phy drive memory");
+        return FALSE;
+    }
+    memset(g_PhyDriveList, 0, sizeof(PHY_DRIVE_INFO)* VENTOY_MAX_PHY_DRIVE);
+
+    GetAllPhysicalDriveInfo(g_PhyDriveList, &g_PhyDriveCount);
+    FilterPhysicalDrive(g_PhyDriveList, g_PhyDriveCount);
+
+    return 0;
+}
+
+int Ventoy2DiskDestroy(void)
+{
+    free(g_PhyDriveList);
+    return 0;
+}
diff --git a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.h b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.h
new file mode 100644 (file)
index 0000000..ad8bb52
--- /dev/null
@@ -0,0 +1,163 @@
+/******************************************************************************
+ * Ventoy2Disk.h
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __VENTOY2DISK_H__
+#define __VENTOY2DISK_H__
+
+#include <stdio.h>
+
+#define SIZE_1MB                    (1024 * 1024)
+#define VENTOY_EFI_PART_SIZE       (32 * SIZE_1MB)
+#define VENTOY_PART1_START_SECTOR    2048
+
+#define VENTOY_FILE_BOOT_IMG    "boot\\boot.img"
+#define VENTOY_FILE_STG1_IMG    "boot\\core.img.xz"
+#define VENTOY_FILE_DISK_IMG    "ventoy\\ventoy.disk.img.xz"
+#define VENTOY_FILE_LOG         "log.txt"
+#define VENTOY_FILE_VERSION     "ventoy\\version"
+
+#define DRIVE_ACCESS_TIMEOUT        15000              // How long we should retry drive access (in ms)
+#define DRIVE_ACCESS_RETRIES        150                        // How many times we should retry
+
+#define IsFileExist(Fmt, ...) IsPathExist(FALSE, Fmt, __VA_ARGS__)
+#define IsDirExist(Fmt, ...)  IsPathExist(TRUE, Fmt, __VA_ARGS__)
+
+#define safe_sprintf(dst, fmt, ...) sprintf_s(dst, sizeof(dst), fmt, __VA_ARGS__)
+#define safe_strcpy(dst, src)  strcpy_s(dst, sizeof(dst), src)
+
+#define CHECK_CLOSE_HANDLE(Handle) \
+{\
+    if (Handle != INVALID_HANDLE_VALUE) \
+    {\
+        CloseHandle(Handle); \
+        Handle = INVALID_HANDLE_VALUE; \
+    }\
+}
+
+#define LASTERR     GetLastError()
+
+#pragma pack(1)
+typedef struct PART_TABLE
+{
+    UINT8  Active; // 0x00  0x80
+
+    UINT8  StartHead;
+    UINT16 StartSector : 6;
+    UINT16 StartCylinder : 10;
+
+    UINT8  FsFlag;
+
+    UINT8  EndHead;
+    UINT16 EndSector : 6;
+    UINT16 EndCylinder : 10;
+
+    UINT32 StartSectorId;
+    UINT32 SectorCount;
+}PART_TABLE;
+
+typedef struct MBR_HEAD
+{
+    UINT8 BootCode[446];
+    PART_TABLE PartTbl[4];
+    UINT8 Byte55;
+    UINT8 ByteAA;
+}MBR_HEAD;
+#pragma pack()
+
+#define VENTOY_MAX_PHY_DRIVE  128
+
+typedef struct PHY_DRIVE_INFO
+{
+    int Id;
+    int PhyDrive;
+    UINT64 SizeInBytes;
+    BYTE DeviceType;
+    BOOL RemovableMedia;
+    CHAR VendorId[128];
+    CHAR ProductId[128];
+    CHAR ProductRev[128];
+    CHAR SerialNumber[128];
+    STORAGE_BUS_TYPE BusType;
+
+    int FirstDriveLetter;
+    CHAR VentoyVersion[32];
+
+}PHY_DRIVE_INFO;
+
+typedef enum PROGRESS_POINT
+{
+    PT_START = 0,
+    PT_LOCK_FOR_CLEAN,
+    PT_DEL_ALL_PART,
+    PT_LOCK_FOR_WRITE,
+    PT_FORMAT_PART1,
+    PT_LOCK_VOLUME = PT_FORMAT_PART1,
+    PT_FORMAT_PART2,
+
+    PT_WRITE_VENTOY_START,
+    PT_WRITE_VENTOY_FINISH = PT_WRITE_VENTOY_START + 32,
+
+    PT_WRITE_STG1_IMG,
+    PT_WRITE_PART_TABLE,
+    PT_MOUNT_VOLUME,
+
+    PT_FINISH
+}PROGRESS_POINT;
+
+#define PROGRESS_BAR_SET_POS(pos)  SetProgressBarPos(pos)
+
+extern PHY_DRIVE_INFO *g_PhyDriveList;
+extern DWORD g_PhyDriveCount;
+extern int g_ForceOperation;
+extern HWND g_ProgressBarHwnd;
+
+void Log(const char *Fmt, ...);
+BOOL IsPathExist(BOOL Dir, const char *Fmt, ...);
+void DumpWindowsVersion(void);
+const CHAR* GetLocalVentoyVersion(void);
+const CHAR* ParseVentoyVersionFromString(CHAR *Buf);
+CHAR GetFirstUnusedDriveLetter(void);
+const CHAR * GetBusTypeString(STORAGE_BUS_TYPE Type);
+int VentoyGetLocalBootImg(MBR_HEAD *pMBR);
+int GetHumanReadableGBSize(UINT64 SizeBytes);
+void TrimString(CHAR *String);
+int VentoyFillMBR(UINT64 DiskSizeBytes, MBR_HEAD *pMBR);
+BOOL IsVentoyLogicalDrive(CHAR DriveLetter);
+int GetRegDwordValue(HKEY Key, LPCSTR SubKey, LPCSTR ValueName, DWORD *pValue);
+int GetPhysicalDriveCount(void);
+int GetAllPhysicalDriveInfo(PHY_DRIVE_INFO *pDriveList, DWORD *pDriveCount);
+int GetPhyDriveByLogicalDrive(int DriveLetter);
+int GetVentoyVerInPhyDrive(const PHY_DRIVE_INFO *pDriveInfo, CHAR *VerBuf, size_t BufLen);
+int Ventoy2DiskInit(void);
+int Ventoy2DiskDestroy(void);
+PHY_DRIVE_INFO * GetPhyDriveInfoById(int Id);
+int ParseCmdLineOption(LPSTR lpCmdLine);
+int InstallVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive);
+int UpdateVentoy2PhyDrive(PHY_DRIVE_INFO *pPhyDrive);
+void SetProgressBarPos(int Pos);
+int ReadWholeFileToBuf(const CHAR *FileName, int ExtLen, void **Bufer, int *BufLen);
+int INIT unxz(unsigned char *in, int in_size,
+    int(*fill)(void *dest, unsigned int size),
+    int(*flush)(void *src, unsigned int size),
+    unsigned char *out, int *in_used,
+    void(*error)(char *x));
+void disk_io_set_param(HANDLE Handle, UINT64 SectorCount);
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.rc b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.rc
new file mode 100644 (file)
index 0000000..1f393ad
Binary files /dev/null and b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.rc differ
diff --git a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj
new file mode 100644 (file)
index 0000000..4f8767b
--- /dev/null
@@ -0,0 +1,142 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{8D231B30-65B1-48A2-A720-F659E61DD390}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>Ventoy2Disk</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>Unicode</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <EmbedManifest>true</EmbedManifest>
+    <IncludePath>$(ProjectDir)\fat_io_lib\;$(ProjectDir)\xz-embedded-20130513\linux\include;$(ProjectDir)\xz-embedded-20130513\linux\include\linux;$(ProjectDir)\xz-embedded-20130513\userspace;$(ProjectDir)\ff14\source;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+    <IncludePath>$(ProjectDir)\fat_io_lib\;$(ProjectDir)\xz-embedded-20130513\linux\include;$(ProjectDir)\xz-embedded-20130513\linux\include\linux;$(ProjectDir)\xz-embedded-20130513\userspace;$(ProjectDir)\ff14\source;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>FATFS_INC_FORMAT_SUPPORT=0;STATIC=static;INIT=;WIN32;_DEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
+      <AdditionalDependencies>version.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <Manifest>
+      <AdditionalManifestFiles>$(ProjectDir)\Res\Ventoy2Disk.manifest %(AdditionalManifestFiles)</AdditionalManifestFiles>
+    </Manifest>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>FATFS_INC_FORMAT_SUPPORT=0;STATIC=static;INIT=;WIN32;NDEBUG;_WINDOWS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+    </ClCompile>
+    <Link>
+      <SubSystem>Windows</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <UACExecutionLevel>RequireAdministrator</UACExecutionLevel>
+      <AdditionalDependencies>version.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+    <Manifest>
+      <AdditionalManifestFiles>$(ProjectDir)\Res\Ventoy2Disk.manifest %(AdditionalManifestFiles)</AdditionalManifestFiles>
+    </Manifest>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="fat_io_lib\fat_access.c" />
+    <ClCompile Include="fat_io_lib\fat_cache.c" />
+    <ClCompile Include="fat_io_lib\fat_filelib.c" />
+    <ClCompile Include="fat_io_lib\fat_format.c" />
+    <ClCompile Include="fat_io_lib\fat_misc.c" />
+    <ClCompile Include="fat_io_lib\fat_string.c" />
+    <ClCompile Include="fat_io_lib\fat_table.c" />
+    <ClCompile Include="fat_io_lib\fat_write.c" />
+    <ClCompile Include="ff14\source\diskio.c" />
+    <ClCompile Include="ff14\source\ff.c" />
+    <ClCompile Include="ff14\source\ffsystem.c" />
+    <ClCompile Include="ff14\source\ffunicode.c" />
+    <ClCompile Include="Language.c" />
+    <ClCompile Include="PhyDrive.c" />
+    <ClCompile Include="Utility.c" />
+    <ClCompile Include="Ventoy2Disk.c" />
+    <ClCompile Include="WinDialog.c" />
+    <ClCompile Include="xz-embedded-20130513\linux\lib\decompress_unxz.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="fat_io_lib\fat_access.h" />
+    <ClInclude Include="fat_io_lib\fat_cache.h" />
+    <ClInclude Include="fat_io_lib\fat_defs.h" />
+    <ClInclude Include="fat_io_lib\fat_filelib.h" />
+    <ClInclude Include="fat_io_lib\fat_format.h" />
+    <ClInclude Include="fat_io_lib\fat_list.h" />
+    <ClInclude Include="fat_io_lib\fat_misc.h" />
+    <ClInclude Include="fat_io_lib\fat_opts.h" />
+    <ClInclude Include="fat_io_lib\fat_string.h" />
+    <ClInclude Include="fat_io_lib\fat_table.h" />
+    <ClInclude Include="fat_io_lib\fat_types.h" />
+    <ClInclude Include="fat_io_lib\fat_write.h" />
+    <ClInclude Include="ff14\source\diskio.h" />
+    <ClInclude Include="ff14\source\ff.h" />
+    <ClInclude Include="ff14\source\ffconf.h" />
+    <ClInclude Include="Language.h" />
+    <ClInclude Include="resource.h" />
+    <ClInclude Include="Ventoy2Disk.h" />
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="Ventoy2Disk.rc" />
+  </ItemGroup>
+  <ItemGroup>
+    <Image Include="Res\ventoy.ico" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj.filters b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj.filters
new file mode 100644 (file)
index 0000000..58da7af
--- /dev/null
@@ -0,0 +1,139 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="源文件">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="头文件">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="资源文件">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="Ventoy2Disk.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="Utility.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="Language.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="PhyDrive.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="fat_io_lib\fat_access.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="fat_io_lib\fat_cache.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="fat_io_lib\fat_filelib.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="fat_io_lib\fat_format.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="fat_io_lib\fat_misc.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="fat_io_lib\fat_string.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="fat_io_lib\fat_table.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="fat_io_lib\fat_write.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="WinDialog.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="xz-embedded-20130513\linux\lib\decompress_unxz.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="ff14\source\diskio.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="ff14\source\ff.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="ff14\source\ffsystem.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="ff14\source\ffunicode.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="Ventoy2Disk.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="resource.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="Language.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_access.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_cache.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_defs.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_filelib.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_format.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_list.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_misc.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_opts.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_string.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_table.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_types.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_write.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="ff14\source\diskio.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="ff14\source\ff.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="ff14\source\ffconf.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+  </ItemGroup>
+  <ItemGroup>
+    <ResourceCompile Include="Ventoy2Disk.rc">
+      <Filter>资源文件</Filter>
+    </ResourceCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <Image Include="Res\ventoy.ico">
+      <Filter>资源文件</Filter>
+    </Image>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj.user b/Ventoy2Disk/Ventoy2Disk/Ventoy2Disk.vcxproj.user
new file mode 100644 (file)
index 0000000..08fca61
--- /dev/null
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LocalDebuggerWorkingDirectory>C:\share\ventoy-1.0.03</LocalDebuggerWorkingDirectory>
+    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+    <LocalDebuggerCommandArguments>
+    </LocalDebuggerCommandArguments>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LocalDebuggerWorkingDirectory>C:\share\ventoy-1.0.03</LocalDebuggerWorkingDirectory>
+    <DebuggerFlavor>WindowsLocalDebugger</DebuggerFlavor>
+    <LocalDebuggerCommandArguments>
+    </LocalDebuggerCommandArguments>
+  </PropertyGroup>
+</Project>
\ No newline at end of file
diff --git a/Ventoy2Disk/Ventoy2Disk/WinDialog.c b/Ventoy2Disk/Ventoy2Disk/WinDialog.c
new file mode 100644 (file)
index 0000000..7ee8135
--- /dev/null
@@ -0,0 +1,433 @@
+/******************************************************************************
+ * WinDialog.c
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <Windows.h>
+#include <commctrl.h>
+#include "resource.h"
+#include "Language.h"
+#include "Ventoy2Disk.h"
+
+HINSTANCE g_hInst;
+
+HWND g_DialogHwnd;
+HWND g_ComboxHwnd;
+HWND g_StaticLocalVerHwnd;
+HWND g_StaticDiskVerHwnd;
+HWND g_BtnInstallHwnd;
+HWND g_BtnUpdateHwnd;
+HWND g_ProgressBarHwnd;
+HWND g_StaticStatusHwnd;
+CHAR g_CurVersion[64];
+HANDLE g_ThreadHandle = NULL;
+
+void GetExeVersionInfo(const char *FilePath)
+{
+    UINT length;
+    DWORD verBufferSize;
+    CHAR  verBuffer[2048];
+    VS_FIXEDFILEINFO *verInfo = NULL;
+
+    verBufferSize = GetFileVersionInfoSizeA(FilePath, NULL);
+
+    if (verBufferSize > 0 && verBufferSize <= sizeof(verBuffer))
+    {
+        if (GetFileVersionInfoA(FilePath, 0, verBufferSize, (LPVOID)verBuffer))
+        {
+            VerQueryValueA(verBuffer, "\\", &verInfo, &length);
+
+            safe_sprintf(g_CurVersion, "%u.%u.%u.%u",
+                HIWORD(verInfo->dwProductVersionMS),
+                LOWORD(verInfo->dwProductVersionMS),
+                HIWORD(verInfo->dwProductVersionLS),
+                LOWORD(verInfo->dwProductVersionLS));
+        }
+    }
+}
+
+void SetProgressBarPos(int Pos)
+{
+    CHAR Ratio[64];
+
+    if (Pos >= PT_FINISH)
+    {
+        Pos = PT_FINISH;
+    }
+
+    SendMessage(g_ProgressBarHwnd, PBM_SETPOS, Pos, 0);
+
+    safe_sprintf(Ratio, "Status - %.0lf%%", Pos * 100.0 / PT_FINISH);
+    SetWindowTextA(g_StaticStatusHwnd, Ratio);
+}
+
+static void OnComboxSelChange(HWND hCombox)
+{
+    int nCurSelected;
+    PHY_DRIVE_INFO *CurDrive = NULL;
+
+    SetWindowTextA(g_StaticLocalVerHwnd, GetLocalVentoyVersion());
+    EnableWindow(g_BtnInstallHwnd, FALSE);
+    EnableWindow(g_BtnUpdateHwnd, FALSE);
+
+    nCurSelected = SendMessage(hCombox, CB_GETCURSEL, 0, 0);
+    if (CB_ERR == nCurSelected)
+    {
+        return;
+    }
+
+    CurDrive = GetPhyDriveInfoById(nCurSelected);
+    if (!CurDrive)
+    {
+        return;
+    }
+    
+    SetWindowTextA(g_StaticDiskVerHwnd, CurDrive->VentoyVersion);
+
+    if (g_ForceOperation == 0)
+    {
+        if (CurDrive->VentoyVersion[0])
+        {
+            //only can update
+            EnableWindow(g_BtnInstallHwnd, FALSE);
+            EnableWindow(g_BtnUpdateHwnd, TRUE);
+        }
+        else
+        {
+            //only can install
+            EnableWindow(g_BtnInstallHwnd, TRUE);
+            EnableWindow(g_BtnUpdateHwnd, FALSE);
+        }
+    }
+    else
+    {
+        EnableWindow(g_BtnInstallHwnd, TRUE);
+        EnableWindow(g_BtnUpdateHwnd, TRUE);
+    }
+
+    InvalidateRect(g_DialogHwnd, NULL, TRUE);
+    UpdateWindow(g_DialogHwnd);
+}
+
+static void LanguageInit(void)
+{
+    SetWindowText(GetDlgItem(g_DialogHwnd, IDC_STATIC_DEV), _G(STR_DEVICE));
+    SetWindowText(GetDlgItem(g_DialogHwnd, IDC_STATIC_LOCAL), _G(STR_LOCAL_VER));
+    SetWindowText(GetDlgItem(g_DialogHwnd, IDC_STATIC_DISK), _G(STR_DISK_VER));
+    SetWindowText(g_StaticStatusHwnd, _G(STR_STATUS));
+
+    SetWindowText(g_BtnInstallHwnd, _G(STR_INSTALL));
+    SetWindowText(g_BtnUpdateHwnd, _G(STR_UPDATE));
+}
+
+static BOOL InitDialog(HWND hWnd, WPARAM wParam, LPARAM lParam)
+{
+    DWORD i;
+    HANDLE hCombox;
+    HFONT hStaticFont;
+    CHAR Letter[32];
+    CHAR DeviceName[256];
+    HICON hIcon;
+
+    g_DialogHwnd = hWnd;
+    g_ComboxHwnd = GetDlgItem(hWnd, IDC_COMBO1);
+    g_StaticLocalVerHwnd = GetDlgItem(hWnd, IDC_STATIC_LOCAL_VER);
+    g_StaticDiskVerHwnd = GetDlgItem(hWnd, IDC_STATIC_DISK_VER);
+    g_BtnInstallHwnd = GetDlgItem(hWnd, IDC_BUTTON4);
+    g_BtnUpdateHwnd = GetDlgItem(hWnd, IDC_BUTTON3);
+    g_ProgressBarHwnd = GetDlgItem(hWnd, IDC_PROGRESS1);
+    g_StaticStatusHwnd = GetDlgItem(hWnd, IDC_STATIC_STATUS);
+
+    hIcon = LoadIcon(g_hInst, MAKEINTRESOURCE(IDI_ICON1));
+    SendMessage(hWnd, WM_SETICON, ICON_BIG, (LPARAM)hIcon);
+    SendMessage(hWnd, WM_SETICON, ICON_SMALL, (LPARAM)hIcon);
+
+    SendMessage(g_ProgressBarHwnd, PBM_SETRANGE, (WPARAM)0, (LPARAM)(MAKELPARAM(0, PT_FINISH)));
+    PROGRESS_BAR_SET_POS(PT_START);
+
+    LanguageInit();
+
+    // Fill device combox
+    hCombox = GetDlgItem(hWnd, IDC_COMBO1);
+    for (i = 0; i < g_PhyDriveCount; i++)
+    {
+        if (g_PhyDriveList[i].Id < 0)
+        {
+            continue;
+        }
+
+        if (g_PhyDriveList[i].FirstDriveLetter >= 0)
+        {
+            safe_sprintf(Letter, "%C: ", g_PhyDriveList[i].FirstDriveLetter);
+        }
+        else
+        {
+            Letter[0] = 0;
+        }
+
+        safe_sprintf(DeviceName, "%s[%dGB] %s %s", 
+            Letter,
+            GetHumanReadableGBSize(g_PhyDriveList[i].SizeInBytes),
+            g_PhyDriveList[i].VendorId,
+            g_PhyDriveList[i].ProductId
+            );
+        SendMessageA(hCombox, CB_ADDSTRING, 0, (LPARAM)DeviceName);
+    }
+    
+    SendMessage(hCombox, CB_SETCURSEL, 0, 0);
+
+    // Set static text & font 
+    hStaticFont = CreateFont(26, 0, 0, 0, FW_BOLD, FALSE, FALSE, 0,
+        ANSI_CHARSET, OUT_DEFAULT_PRECIS,
+        CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY,
+        DEFAULT_PITCH&FF_SWISS, TEXT("Courier New"));
+
+    SendMessage(g_StaticLocalVerHwnd, WM_SETFONT, (WPARAM)hStaticFont, TRUE);
+    SendMessage(g_StaticDiskVerHwnd, WM_SETFONT, (WPARAM)hStaticFont, TRUE);
+
+    OnComboxSelChange(g_ComboxHwnd);
+
+    SetFocus(g_ProgressBarHwnd);
+
+    return TRUE;
+}
+
+static DWORD WINAPI InstallVentoyThread(void* Param)
+{
+    int rc;
+    PHY_DRIVE_INFO *pPhyDrive = (PHY_DRIVE_INFO *)Param;
+
+    rc = InstallVentoy2PhyDrive(pPhyDrive);
+    if (rc == 0)
+    {
+               PROGRESS_BAR_SET_POS(PT_FINISH);
+        MessageBox(g_DialogHwnd, _G(STR_INSTALL_SUCCESS), _G(STR_INFO), MB_OK | MB_ICONINFORMATION);
+        safe_strcpy(pPhyDrive->VentoyVersion, GetLocalVentoyVersion());
+    }
+    else
+    {
+               PROGRESS_BAR_SET_POS(PT_FINISH);
+        MessageBox(g_DialogHwnd, _G(STR_INSTALL_FAILED), _G(STR_ERROR), MB_OK | MB_ICONERROR);
+    }
+    
+       PROGRESS_BAR_SET_POS(PT_START);
+    g_ThreadHandle = NULL;
+    SetWindowText(g_StaticStatusHwnd, _G(STR_STATUS));
+    OnComboxSelChange(g_ComboxHwnd);
+
+    return 0;
+}
+
+static DWORD WINAPI UpdateVentoyThread(void* Param)
+{
+    int rc;
+    PHY_DRIVE_INFO *pPhyDrive = (PHY_DRIVE_INFO *)Param;
+
+    rc = UpdateVentoy2PhyDrive(pPhyDrive);
+    if (rc == 0)
+    {
+               PROGRESS_BAR_SET_POS(PT_FINISH);
+        MessageBox(g_DialogHwnd, _G(STR_UPDATE_SUCCESS), _G(STR_INFO), MB_OK | MB_ICONINFORMATION);
+        safe_strcpy(pPhyDrive->VentoyVersion, GetLocalVentoyVersion());
+    }
+    else
+    {
+               PROGRESS_BAR_SET_POS(PT_FINISH);
+        MessageBox(g_DialogHwnd, _G(STR_UPDATE_FAILED), _G(STR_ERROR), MB_OK | MB_ICONERROR);
+    }
+    
+       PROGRESS_BAR_SET_POS(PT_START);
+    g_ThreadHandle = NULL;
+    SetWindowText(g_StaticStatusHwnd, _G(STR_STATUS));
+    OnComboxSelChange(g_ComboxHwnd);
+
+    return 0;
+}
+
+
+
+static void OnInstallBtnClick(void)
+{
+    int nCurSel;
+    PHY_DRIVE_INFO *pPhyDrive = NULL;
+
+    if (MessageBox(g_DialogHwnd, _G(STR_INSTALL_TIP), _G(STR_WARNING), MB_YESNO | MB_ICONWARNING) != IDYES)
+    {
+        return;
+    }
+
+    if (MessageBox(g_DialogHwnd, _G(STR_INSTALL_TIP2), _G(STR_WARNING), MB_YESNO | MB_ICONWARNING) != IDYES)
+    {
+        return;
+    }
+
+    if (g_ThreadHandle)
+    {
+        Log("Another thread is runing");
+        return;
+    }
+
+    nCurSel = SendMessage(g_ComboxHwnd, CB_GETCURSEL, 0, 0);
+    if (CB_ERR == nCurSel)
+    {
+        Log("Failed to get combox sel");
+        return;;
+    }
+
+    pPhyDrive = GetPhyDriveInfoById(nCurSel);
+    if (!pPhyDrive)
+    {
+        return;
+    }
+
+    EnableWindow(g_BtnInstallHwnd, FALSE);
+    EnableWindow(g_BtnUpdateHwnd, FALSE);
+
+    g_ThreadHandle = CreateThread(NULL, 0, InstallVentoyThread, (LPVOID)pPhyDrive, 0, NULL);
+}
+
+
+
+static void OnUpdateBtnClick(void)
+{
+    int nCurSel;
+    PHY_DRIVE_INFO *pPhyDrive = NULL;
+
+    if (MessageBox(g_DialogHwnd, _G(STR_UPDATE_TIP), _G(STR_INFO), MB_YESNO | MB_ICONQUESTION) != IDYES)
+    {
+        return;
+    }
+
+    if (g_ThreadHandle)
+    {
+        Log("Another thread is runing");
+        return;
+    }
+
+    nCurSel = SendMessage(g_ComboxHwnd, CB_GETCURSEL, 0, 0);
+    if (CB_ERR == nCurSel)
+    {
+        Log("Failed to get combox sel");
+        return;;
+    }
+
+    pPhyDrive = GetPhyDriveInfoById(nCurSel);
+    if (!pPhyDrive)
+    {
+        return;
+    }
+
+    EnableWindow(g_BtnInstallHwnd, FALSE);
+    EnableWindow(g_BtnUpdateHwnd, FALSE);
+
+    g_ThreadHandle = CreateThread(NULL, 0, UpdateVentoyThread, (LPVOID)pPhyDrive, 0, NULL);
+}
+
+
+INT_PTR CALLBACK DialogProc(HWND hWnd, UINT Message, WPARAM wParam, LPARAM lParam)
+{
+    WORD NotifyCode;
+    WORD CtrlID;
+
+    switch (Message)
+    {
+        case WM_COMMAND:
+        {
+            NotifyCode = HIWORD(wParam);
+            CtrlID = LOWORD(wParam);
+
+            if (CtrlID == IDC_COMBO1 && NotifyCode == CBN_SELCHANGE)
+            {
+                OnComboxSelChange((HWND)lParam);
+            }
+
+            if (CtrlID == IDC_BUTTON4 && NotifyCode == BN_CLICKED)
+            {
+                OnInstallBtnClick();
+            }
+            else if (CtrlID == IDC_BUTTON3 && NotifyCode == BN_CLICKED)
+            {
+                OnUpdateBtnClick();
+            }
+
+
+            break;
+        }
+        case WM_INITDIALOG:
+        {
+            InitDialog(hWnd, wParam, lParam);
+            break;
+        }
+        case WM_CTLCOLORSTATIC:
+        {
+            if (GetDlgItem(hWnd, IDC_STATIC_LOCAL_VER) == (HANDLE)lParam || 
+                GetDlgItem(hWnd, IDC_STATIC_DISK_VER) == (HANDLE)lParam)
+            {
+                SetBkMode((HDC)wParam, TRANSPARENT);
+                SetTextColor((HDC)wParam, RGB(255, 0, 0));
+                return (LRESULT)(HBRUSH)(GetStockObject(HOLLOW_BRUSH));
+            }
+            else
+            {
+                break;
+            }
+        }
+        case WM_CLOSE:
+        {
+            if (g_ThreadHandle)
+            {
+                MessageBox(g_DialogHwnd, _G(STR_WAIT_PROCESS), _G(STR_INFO), MB_OK | MB_ICONINFORMATION);
+            }
+            else
+            {
+                EndDialog(hWnd, 0);
+            }
+            break;
+        }
+    }
+
+    return 0;
+}
+
+int APIENTRY WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, INT nCmdShow)
+{
+    UNREFERENCED_PARAMETER(hPrevInstance);
+
+    if (!IsFileExist(VENTOY_FILE_VERSION))
+    {
+        MessageBox(NULL, _G(STR_INCORRECT_DIR), _G(STR_ERROR), MB_OK | MB_ICONERROR);
+        return ERROR_NOT_FOUND;
+    }
+
+    GetExeVersionInfo(__argv[0]);
+
+    Log("\n################################ Ventoy2Disk %s ################################", g_CurVersion);
+
+    ParseCmdLineOption(lpCmdLine);
+
+    DumpWindowsVersion();
+
+    Ventoy2DiskInit();
+
+    g_hInst = hInstance;
+    DialogBox(hInstance, MAKEINTRESOURCE(IDD_DIALOG1), NULL, DialogProc);
+
+    Ventoy2DiskDestroy();
+
+    return 0;
+}
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/API.txt b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/API.txt
new file mode 100644 (file)
index 0000000..666cbe5
--- /dev/null
@@ -0,0 +1,22 @@
+File IO Lib API
+-=-=-=-=-=-=-=-=-
+
+void fl_init(void)
+
+  Called to initialize FAT IO library.
+  This should be called prior to any other functions.
+
+void fl_attach_locks(void (*lock)(void), void (*unlock)(void))
+
+  [Optional] File system thread safety locking functions.
+  For thread safe operation, you should provide lock() and unlock() functions.
+  Note that locking primitive used must support recursive locking, i.e lock() called within an already \91locked\92 region.
+
+int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)
+
+  This function is used to attach system specific disk/media access functions.  
+  This should be done subsequent to calling fl_init() and fl_attach_locks() (if locking required).
+
+void fl_shutdown(void)
+
+  Shutdown the FAT IO library. This purges any un-saved data back to disk.
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/COPYRIGHT.txt b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/COPYRIGHT.txt
new file mode 100644 (file)
index 0000000..0d728f3
--- /dev/null
@@ -0,0 +1,345 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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.
+
+  <signature of Ty Coon>, 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.
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/Configuration.txt b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/Configuration.txt
new file mode 100644 (file)
index 0000000..370f1da
--- /dev/null
@@ -0,0 +1,53 @@
+File IO Lib Options
+-=-=-=-=-=-=-=-=-=-
+
+See defines in fat_opts.h:
+
+FATFS_IS_LITTLE_ENDIAN         [1/0]
+  Which endian is your system? Set to 1 for little endian, 0 for big endian.
+
+FATFS_MAX_LONG_FILENAME        [260]
+  By default, 260 characters (max LFN length). Increase this to support greater path depths.
+
+FATFS_MAX_OPEN_FILES   
+  The more files you wish to have concurrently open, the greater this number should be.
+  This increases the number of FL_FILE file structures in the library, each of these is around 1K in size (assuming 512 byte sectors).
+
+FAT_BUFFER_SECTORS
+  Minimum is 1, more increases performance.
+  This defines how many FAT sectors can be buffered per FAT_BUFFER entry.
+
+FAT_BUFFERS
+  Minimum is 1, more increases performance.
+  This defines how many FAT buffer entries are available.
+  Memory usage is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE
+
+FATFS_INC_WRITE_SUPPORT
+  Support file write functionality.
+
+FAT_SECTOR_SIZE
+  Sector size used by buffers. Most likely to be 512 bytes (standard for ATA/IDE).
+
+FAT_PRINTF
+  A define that allows the File IO library to print to console/stdout. 
+  Provide your own printf function if printf not available.
+
+FAT_CLUSTER_CACHE_ENTRIES
+  Size of cluster chain cache (can be undefined if not required).
+  Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2
+  Improves access speed considerably.
+
+FATFS_INC_LFN_SUPPORT  [1/0]
+  Enable/Disable support for long filenames.
+
+FATFS_DIR_LIST_SUPPORT         [1/0]
+  Include support for directory listing.
+
+FATFS_INC_TIME_DATE_SUPPORT  [1/0]
+  Use time/date functions provided by time.h to update creation & modification timestamps.
+
+FATFS_INC_FORMAT_SUPPORT
+  Include support for formatting disks (FAT16 only).
+
+FAT_PRINTF_NOINC_STDIO
+  Disable use of printf & inclusion of stdio.h
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/History.txt b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/History.txt
new file mode 100644 (file)
index 0000000..b0bdc90
--- /dev/null
@@ -0,0 +1,24 @@
+Revision History
+-=-=-=-=-=-=-=-=-
+v2.6.11 - Fix compilation with GCC on 64-bit machines
+v2.6.10 - Added support for FAT32 format.
+V2.6.9  - Added support for time & date handling.
+V2.6.8  - Fixed error with FSINFO sector write.
+V2.6.7  - Added fgets().
+          Fixed C warnings, removed dependancy on some string.h functions.
+V2.6.6  \96 Massive read + write performance  improvements.
+V2.6.5  \96 Bug fixes for big endian systems.
+V2.6.4  \96 Further bug fixes and performance improvements for write operations.
+V2.6.3  \96 Peformance improvements, FAT16 formatting support. Various bug fixes.
+V2.6    - Basic support for FAT16 added (18-04-10).
+V2.5    - Code cleaned up. Many bugs fixed. Thread safety functions added.
+V2.x    - Write support added as well as better stdio like API.
+V1.0    - Rewrite of all code to enable multiple files to be opened and provides a 
+          better file API.
+         Also better string matching, and generally better C code than origonal 
+          version.
+V0.1c   - Fetch_ID_Max_LBA() function added to retrieve Drive infomation and stoping 
+          the drive reads from addressing a sector that is out of range.
+V0.1b   - fopen(), fgetc(), fopenDIR() using new software stack for IDE and FAT32 
+          access.
+V0.1a   - First release (27/12/03); fopen(), fgetc() unbuffered reads.
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/License.txt b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/License.txt
new file mode 100644 (file)
index 0000000..3e78364
--- /dev/null
@@ -0,0 +1,10 @@
+FAT File IO Library License
+-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+This versions license: GPL
+
+If you include GPL software in your project, you must release the source code of that project too.
+
+If you would like a version with a more permissive license for use in closed source commercial applications please contact me for details.
+
+Email: admin@ultra-embedded.com
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/Media b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/Media
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/Media Access API.txt b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/Media Access API.txt
new file mode 100644 (file)
index 0000000..14ceed3
--- /dev/null
@@ -0,0 +1,40 @@
+Media Access API
+-=-=-=-=-=-=-=-=-
+
+int media_read(uint32 sector, uint8 *buffer, uint32 sector_count)
+
+Params:
+       Sector: 32-bit sector number
+       Buffer: Target buffer to read n sectors of data into.
+       Sector_count: Number of sectors to read.
+
+Return: 
+       int, 1 = success, 0 = failure.
+
+Description:
+   Application/target specific disk/media read function.
+   Sector number (sectors are usually 512 byte pages) to read.
+
+Media Write API
+
+int media_write(uint32 sector, uint8 *buffer, uint32 sector_count)
+
+Params:
+       Sector: 32-bit sector number
+       Buffer: Target buffer to write n sectors of data from.
+       Sector_count: Number of sectors to write.
+
+Return: 
+       int, 1 = success, 0 = failure.
+
+Description:
+   Application/target specific disk/media write function.
+   Sector number (sectors are usually 512 byte pages) to write to.
+
+File IO Library Linkage
+   Use the following API to attach the media IO functions to the File IO library.
+
+   int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)
+
+
+
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/example.c.txt b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/example.c.txt
new file mode 100644 (file)
index 0000000..340f0bd
--- /dev/null
@@ -0,0 +1,87 @@
+#include <stdio.h>
+#include "fat_filelib.h"
+
+int media_init()
+{
+    // ...
+    return 1;
+}
+
+int media_read(unsigned long sector, unsigned char *buffer, unsigned long sector_count)
+{
+    unsigned long i;
+
+    for (i=0;i<sector_count;i++)
+    {
+        // ...
+        // Add platform specific sector (512 bytes) read code here
+        //..
+
+        sector ++;
+        buffer += 512;
+    }
+
+    return 1;
+}
+
+int media_write(unsigned long sector, unsigned char *buffer, unsigned long sector_count)
+{
+    unsigned long i;
+
+    for (i=0;i<sector_count;i++)
+    {
+        // ...
+        // Add platform specific sector (512 bytes) write code here
+        //..
+
+        sector ++;
+        buffer += 512;
+    }
+
+    return 1;
+}
+
+void main()
+{
+    FL_FILE *file;
+
+    // Initialise media
+    media_init();
+
+    // Initialise File IO Library
+    fl_init();
+
+    // Attach media access functions to library
+    if (fl_attach_media(media_read, media_write) != FAT_INIT_OK)
+    {
+        printf("ERROR: Media attach failed\n");
+        return; 
+    }
+
+    // List root directory
+    fl_listdirectory("/");
+
+    // Create File
+    file = fl_fopen("/file.bin", "w");
+    if (file)
+    {
+        // Write some data
+        unsigned char data[] = { 1, 2, 3, 4 };
+        if (fl_fwrite(data, 1, sizeof(data), file) != sizeof(data))
+            printf("ERROR: Write file failed\n");
+    }
+    else
+        printf("ERROR: Create file failed\n");
+
+    // Close file
+    fl_fclose(file);
+
+    // Delete File
+    if (fl_remove("/file.bin") < 0)
+        printf("ERROR: Delete file failed\n");
+
+    // List root directory
+    fl_listdirectory("/");
+
+    fl_shutdown();
+}
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_access.c b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_access.c
new file mode 100644 (file)
index 0000000..0f0cdd5
--- /dev/null
@@ -0,0 +1,904 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//                            FAT16/32 File IO Library
+//                                    V2.6
+//                              Ultra-Embedded.com
+//                            Copyright 2003 - 2012
+//
+//                         Email: admin@ultra-embedded.com
+//
+//                                License: GPL
+//   If you would like a version with a more permissive license for use in
+//   closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_init: Load FAT Parameters
+//-----------------------------------------------------------------------------
+int fatfs_init(struct fatfs *fs)
+{
+    uint8 num_of_fats;
+    uint16 reserved_sectors;
+    uint32 FATSz;
+    uint32 root_dir_sectors;
+    uint32 total_sectors;
+    uint32 data_sectors;
+    uint32 count_of_clusters;
+    uint8 valid_partition = 0;
+
+    fs->currentsector.address = FAT32_INVALID_CLUSTER;
+    fs->currentsector.dirty = 0;
+
+    fs->next_free_cluster = 0; // Invalid
+
+    fatfs_fat_init(fs);
+
+    // Make sure we have a read function (write function is optional)
+    if (!fs->disk_io.read_media)
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // MBR: Sector 0 on the disk
+    // NOTE: Some removeable media does not have this.
+
+    // Load MBR (LBA 0) into the 512 byte buffer
+    if (!fs->disk_io.read_media(0, fs->currentsector.sector, 1))
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // Make Sure 0x55 and 0xAA are at end of sector
+    // (this should be the case regardless of the MBR or boot sector)
+    if (fs->currentsector.sector[SIGNATURE_POSITION] != 0x55 || fs->currentsector.sector[SIGNATURE_POSITION+1] != 0xAA)
+        return FAT_INIT_INVALID_SIGNATURE;
+
+    // Now check again using the access function to prove endian conversion function
+    if (GET_16BIT_WORD(fs->currentsector.sector, SIGNATURE_POSITION) != SIGNATURE_VALUE)
+        return FAT_INIT_ENDIAN_ERROR;
+
+    // Verify packed structures
+    if (sizeof(struct fat_dir_entry) != FAT_DIR_ENTRY_SIZE)
+        return FAT_INIT_STRUCT_PACKING;
+
+    // Check the partition type code
+    switch(fs->currentsector.sector[PARTITION1_TYPECODE_LOCATION])
+    {
+        case 0x0B:
+        case 0x06:
+        case 0x0C:
+        case 0x0E:
+        case 0x0F:
+        case 0x05:
+            valid_partition = 1;
+        break;
+        case 0x00:
+            valid_partition = 0;
+            break;
+        default:
+            if (fs->currentsector.sector[PARTITION1_TYPECODE_LOCATION] <= 0x06)
+                valid_partition = 1;
+        break;
+    }
+
+    // Read LBA Begin for the file system
+    if (valid_partition)
+        fs->lba_begin = GET_32BIT_WORD(fs->currentsector.sector, PARTITION1_LBA_BEGIN_LOCATION);
+    // Else possibly MBR less disk
+    else
+        fs->lba_begin = 0;
+
+    // Load Volume 1 table into sector buffer
+    // (We may already have this in the buffer if MBR less drive!)
+    if (!fs->disk_io.read_media(fs->lba_begin, fs->currentsector.sector, 1))
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // Make sure there are 512 bytes per cluster
+    if (GET_16BIT_WORD(fs->currentsector.sector, 0x0B) != FAT_SECTOR_SIZE)
+        return FAT_INIT_INVALID_SECTOR_SIZE;
+
+    // Load Parameters of FAT partition
+    fs->sectors_per_cluster = fs->currentsector.sector[BPB_SECPERCLUS];
+    reserved_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_RSVDSECCNT);
+    num_of_fats = fs->currentsector.sector[BPB_NUMFATS];
+    fs->root_entry_count = GET_16BIT_WORD(fs->currentsector.sector, BPB_ROOTENTCNT);
+
+    if(GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16) != 0)
+        fs->fat_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16);
+    else
+        fs->fat_sectors = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_FATSZ32);
+
+    // For FAT32 (which this may be)
+    fs->rootdir_first_cluster = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_ROOTCLUS);
+    fs->fs_info_sector = GET_16BIT_WORD(fs->currentsector.sector, BPB_FAT32_FSINFO);
+
+    // For FAT16 (which this may be), rootdir_first_cluster is actuall rootdir_first_sector
+    fs->rootdir_first_sector = reserved_sectors + (num_of_fats * fs->fat_sectors);
+    fs->rootdir_sectors = ((fs->root_entry_count * 32) + (FAT_SECTOR_SIZE - 1)) / FAT_SECTOR_SIZE;
+
+    // First FAT LBA address
+    fs->fat_begin_lba = fs->lba_begin + reserved_sectors;
+
+    // The address of the first data cluster on this volume
+    fs->cluster_begin_lba = fs->fat_begin_lba + (num_of_fats * fs->fat_sectors);
+
+    if (GET_16BIT_WORD(fs->currentsector.sector, 0x1FE) != 0xAA55) // This signature should be AA55
+        return FAT_INIT_INVALID_SIGNATURE;
+
+    // Calculate the root dir sectors
+    root_dir_sectors = ((GET_16BIT_WORD(fs->currentsector.sector, BPB_ROOTENTCNT) * 32) + (GET_16BIT_WORD(fs->currentsector.sector, BPB_BYTSPERSEC) - 1)) / GET_16BIT_WORD(fs->currentsector.sector, BPB_BYTSPERSEC);
+
+    if(GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16) != 0)
+        FATSz = GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16);
+    else
+        FATSz = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_FATSZ32);
+
+    if(GET_16BIT_WORD(fs->currentsector.sector, BPB_TOTSEC16) != 0)
+        total_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_TOTSEC16);
+    else
+        total_sectors = GET_32BIT_WORD(fs->currentsector.sector, BPB_TOTSEC32);
+
+    data_sectors = total_sectors - (GET_16BIT_WORD(fs->currentsector.sector, BPB_RSVDSECCNT) + (fs->currentsector.sector[BPB_NUMFATS] * FATSz) + root_dir_sectors);
+
+    // Find out which version of FAT this is...
+    if (fs->sectors_per_cluster != 0)
+    {
+        count_of_clusters = data_sectors / fs->sectors_per_cluster;
+
+        if(count_of_clusters < 4085)
+            // Volume is FAT12
+            return FAT_INIT_WRONG_FILESYS_TYPE;
+        else if(count_of_clusters < 65525)
+        {
+            // Clear this FAT32 specific param
+            fs->rootdir_first_cluster = 0;
+
+            // Volume is FAT16
+            fs->fat_type = FAT_TYPE_16;
+            return FAT_INIT_OK;
+        }
+        else
+        {
+            // Volume is FAT32
+            fs->fat_type = FAT_TYPE_32;
+            return FAT_INIT_OK;
+        }
+    }
+    else
+        return FAT_INIT_WRONG_FILESYS_TYPE;
+}
+//-----------------------------------------------------------------------------
+// fatfs_lba_of_cluster: This function converts a cluster number into a sector /
+// LBA number.
+//-----------------------------------------------------------------------------
+uint32 fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number)
+{
+    if (fs->fat_type == FAT_TYPE_16)
+        return (fs->cluster_begin_lba + (fs->root_entry_count * 32 / FAT_SECTOR_SIZE) + ((Cluster_Number-2) * fs->sectors_per_cluster));
+    else
+        return ((fs->cluster_begin_lba + ((Cluster_Number-2)*fs->sectors_per_cluster)));
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_read:
+//-----------------------------------------------------------------------------
+int fatfs_sector_read(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count)
+{
+    return fs->disk_io.read_media(lba, target, count);
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_write:
+//-----------------------------------------------------------------------------
+int fatfs_sector_write(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count)
+{
+    return fs->disk_io.write_media(lba, target, count);
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_reader: From the provided startcluster and sector offset
+// Returns True if success, returns False if not (including if read out of range)
+//-----------------------------------------------------------------------------
+int fatfs_sector_reader(struct fatfs *fs, uint32 start_cluster, uint32 offset, uint8 *target)
+{
+    uint32 sector_to_read = 0;
+    uint32 cluster_to_read = 0;
+    uint32 cluster_chain = 0;
+    uint32 i;
+    uint32 lba;
+
+    // FAT16 Root directory
+    if (fs->fat_type == FAT_TYPE_16 && start_cluster == 0)
+    {
+        if (offset < fs->rootdir_sectors)
+            lba = fs->lba_begin + fs->rootdir_first_sector + offset;
+        else
+            return 0;
+    }
+    // FAT16/32 Other
+    else
+    {
+        // Set start of cluster chain to initial value
+        cluster_chain = start_cluster;
+
+        // Find parameters
+        cluster_to_read = offset / fs->sectors_per_cluster;
+        sector_to_read = offset - (cluster_to_read*fs->sectors_per_cluster);
+
+        // Follow chain to find cluster to read
+        for (i=0; i<cluster_to_read; i++)
+            cluster_chain = fatfs_find_next_cluster(fs, cluster_chain);
+
+        // If end of cluster chain then return false
+        if (cluster_chain == FAT32_LAST_CLUSTER)
+            return 0;
+
+        // Calculate sector address
+        lba = fatfs_lba_of_cluster(fs, cluster_chain)+sector_to_read;
+    }
+
+    // User provided target array
+    if (target)
+        return fs->disk_io.read_media(lba, target, 1);
+    // Else read sector if not already loaded
+    else if (lba != fs->currentsector.address)
+    {
+        fs->currentsector.address = lba;
+        return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
+    }
+    else
+        return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_read_sector: Read from the provided cluster and sector offset
+// Returns True if success, returns False if not
+//-----------------------------------------------------------------------------
+int fatfs_read_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target)
+{
+    // FAT16 Root directory
+    if (fs->fat_type == FAT_TYPE_16 && cluster == 0)
+    {
+        uint32 lba;
+
+        // In FAT16, there are a limited amount of sectors in root dir!
+        if (sector < fs->rootdir_sectors)
+            lba = fs->lba_begin + fs->rootdir_first_sector + sector;
+        else
+            return 0;
+
+        // User target buffer passed in
+        if (target)
+        {
+            // Read from disk
+            return fs->disk_io.read_media(lba, target, 1);
+        }
+        else
+        {
+            // Calculate read address
+            fs->currentsector.address = lba;
+
+            // Read from disk
+            return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
+        }
+    }
+    // FAT16/32 Other
+    else
+    {
+        // User target buffer passed in
+        if (target)
+        {
+            // Calculate read address
+            uint32 lba = fatfs_lba_of_cluster(fs, cluster) + sector;
+
+            // Read from disk
+            return fs->disk_io.read_media(lba, target, 1);
+        }
+        else
+        {
+            // Calculate write address
+            fs->currentsector.address = fatfs_lba_of_cluster(fs, cluster)+sector;
+
+            // Read from disk
+            return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
+        }
+    }
+}
+//-----------------------------------------------------------------------------
+// fatfs_write_sector: Write to the provided cluster and sector offset
+// Returns True if success, returns False if not
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_write_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target)
+{
+    // No write access?
+    if (!fs->disk_io.write_media)
+        return 0;
+
+    // FAT16 Root directory
+    if (fs->fat_type == FAT_TYPE_16 && cluster == 0)
+    {
+        uint32 lba;
+
+        // In FAT16 we cannot extend the root dir!
+        if (sector < fs->rootdir_sectors)
+            lba = fs->lba_begin + fs->rootdir_first_sector + sector;
+        else
+            return 0;
+
+        // User target buffer passed in
+        if (target)
+        {
+            // Write to disk
+            return fs->disk_io.write_media(lba, target, 1);
+        }
+        else
+        {
+            // Calculate write address
+            fs->currentsector.address = lba;
+
+            // Write to disk
+            return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+        }
+    }
+    // FAT16/32 Other
+    else
+    {
+        // User target buffer passed in
+        if (target)
+        {
+            // Calculate write address
+            uint32 lba = fatfs_lba_of_cluster(fs, cluster) + sector;
+
+            // Write to disk
+            return fs->disk_io.write_media(lba, target, 1);
+        }
+        else
+        {
+            // Calculate write address
+            fs->currentsector.address = fatfs_lba_of_cluster(fs, cluster)+sector;
+
+            // Write to disk
+            return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+        }
+    }
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_show_details: Show the details about the filesystem
+//-----------------------------------------------------------------------------
+void fatfs_show_details(struct fatfs *fs)
+{
+    FAT_PRINTF(("FAT details:\r\n"));
+    FAT_PRINTF((" Type =%s", (fs->fat_type == FAT_TYPE_32) ? "FAT32": "FAT16"));
+    FAT_PRINTF((" Root Dir First Cluster = %x\r\n", fs->rootdir_first_cluster));
+    FAT_PRINTF((" FAT Begin LBA = 0x%x\r\n",fs->fat_begin_lba));
+    FAT_PRINTF((" Cluster Begin LBA = 0x%x\r\n",fs->cluster_begin_lba));
+    FAT_PRINTF((" Sectors Per Cluster = %d\r\n", fs->sectors_per_cluster));
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_root_cluster: Get the root dir cluster
+//-----------------------------------------------------------------------------
+uint32 fatfs_get_root_cluster(struct fatfs *fs)
+{
+    // NOTE: On FAT16 this will be 0 which has a special meaning...
+    return fs->rootdir_first_cluster;
+}
+//-------------------------------------------------------------
+// fatfs_get_file_entry: Find the file entry for a filename
+//-------------------------------------------------------------
+uint32 fatfs_get_file_entry(struct fatfs *fs, uint32 Cluster, char *name_to_find, struct fat_dir_entry *sfEntry)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    uint8 i=0;
+    int x=0;
+    char *long_filename = NULL;
+    char short_filename[13];
+    struct lfn_cache lfn;
+    int dotRequired = 0;
+    struct fat_dir_entry *directoryEntry;
+
+    fatfs_lfn_cache_init(&lfn, 1);
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if (fatfs_entry_lfn_text(directoryEntry) )
+                    fatfs_lfn_cache_entry(&lfn, fs->currentsector.sector+recordoffset);
+
+                // If Invalid record found delete any long file name information collated
+                else if (fatfs_entry_lfn_invalid(directoryEntry) )
+                    fatfs_lfn_cache_init(&lfn, 0);
+
+                // Normal SFN Entry and Long text exists
+                else if (fatfs_entry_lfn_exists(&lfn, directoryEntry) )
+                {
+                    long_filename = fatfs_lfn_cache_get(&lfn);
+
+                    // Compare names to see if they match
+                    if (fatfs_compare_names(long_filename, name_to_find))
+                    {
+                        memcpy(sfEntry,directoryEntry,sizeof(struct fat_dir_entry));
+                        return 1;
+                    }
+
+                    fatfs_lfn_cache_init(&lfn, 0);
+                }
+                else
+#endif
+                // Normal Entry, only 8.3 Text
+                if (fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    memset(short_filename, 0, sizeof(short_filename));
+
+                    // Copy name to string
+                    for (i=0; i<8; i++)
+                        short_filename[i] = directoryEntry->Name[i];
+
+                    // Extension
+                    dotRequired = 0;
+                    for (i=8; i<11; i++)
+                    {
+                        short_filename[i+1] = directoryEntry->Name[i];
+                        if (directoryEntry->Name[i] != ' ')
+                            dotRequired = 1;
+                    }
+
+                    // Dot only required if extension present
+                    if (dotRequired)
+                    {
+                        // If not . or .. entry
+                        if (short_filename[0]!='.')
+                            short_filename[8] = '.';
+                        else
+                            short_filename[8] = ' ';
+                    }
+                    else
+                        short_filename[8] = ' ';
+
+                    // Compare names to see if they match
+                    if (fatfs_compare_names(short_filename, name_to_find))
+                    {
+                        memcpy(sfEntry,directoryEntry,sizeof(struct fat_dir_entry));
+                        return 1;
+                    }
+
+                    fatfs_lfn_cache_init(&lfn, 0);
+                }
+            } // End of if
+        }
+        else
+            break;
+    } // End of while loop
+
+    return 0;
+}
+//-------------------------------------------------------------
+// fatfs_sfn_exists: Check if a short filename exists.
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_sfn_exists(struct fatfs *fs, uint32 Cluster, char *shortname)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    int x=0;
+    struct fat_dir_entry *directoryEntry;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if (fatfs_entry_lfn_text(directoryEntry) )
+                    ;
+
+                // If Invalid record found delete any long file name information collated
+                else if (fatfs_entry_lfn_invalid(directoryEntry) )
+                    ;
+                else
+#endif
+                // Normal Entry, only 8.3 Text
+                if (fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    if (strncmp((const char*)directoryEntry->Name, shortname, 11)==0)
+                        return 1;
+                }
+            } // End of if
+        }
+        else
+            break;
+    } // End of while loop
+
+    return 0;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_update_timestamps: Update date/time details
+//-------------------------------------------------------------
+#if FATFS_INC_TIME_DATE_SUPPORT
+int fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access)
+{
+    time_t time_now;
+    struct tm * time_info;
+    uint16 fat_time;
+    uint16 fat_date;
+
+    // Get system time
+    time(&time_now);
+
+    // Convert to local time
+    time_info = localtime(&time_now);
+
+    // Convert time to FAT format
+    fat_time = fatfs_convert_to_fat_time(time_info->tm_hour, time_info->tm_min, time_info->tm_sec);
+
+    // Convert date to FAT format
+    fat_date = fatfs_convert_to_fat_date(time_info->tm_mday, time_info->tm_mon + 1, time_info->tm_year + 1900);
+
+    // Update requested fields
+    if (create)
+    {
+        directoryEntry->CrtTime[1] = fat_time >> 8;
+        directoryEntry->CrtTime[0] = fat_time >> 0;
+        directoryEntry->CrtDate[1] = fat_date >> 8;
+        directoryEntry->CrtDate[0] = fat_date >> 0;
+    }
+
+    if (modify)
+    {
+        directoryEntry->WrtTime[1] = fat_time >> 8;
+        directoryEntry->WrtTime[0] = fat_time >> 0;
+        directoryEntry->WrtDate[1] = fat_date >> 8;
+        directoryEntry->WrtDate[0] = fat_date >> 0;
+    }
+
+    if (access)
+    {
+        directoryEntry->LstAccDate[1] = fat_time >> 8;
+        directoryEntry->LstAccDate[0] = fat_time >> 0;
+        directoryEntry->LstAccDate[1] = fat_date >> 8;
+        directoryEntry->LstAccDate[0] = fat_date >> 0;
+    }
+
+    return 1;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_update_file_length: Find a SFN entry and update it
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_update_file_length(struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    int x=0;
+    struct fat_dir_entry *directoryEntry;
+
+    // No write access?
+    if (!fs->disk_io.write_media)
+        return 0;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if (fatfs_entry_lfn_text(directoryEntry) )
+                    ;
+
+                // If Invalid record found delete any long file name information collated
+                else if (fatfs_entry_lfn_invalid(directoryEntry) )
+                    ;
+
+                // Normal Entry, only 8.3 Text
+                else
+#endif
+                if (fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    if (strncmp((const char*)directoryEntry->Name, shortname, 11)==0)
+                    {
+                        directoryEntry->FileSize = FAT_HTONL(fileLength);
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                        // Update access / modify time & date
+                        fatfs_update_timestamps(directoryEntry, 0, 1, 1);
+#endif
+
+                        // Update sfn entry
+                        memcpy((uint8*)(fs->currentsector.sector+recordoffset), (uint8*)directoryEntry, sizeof(struct fat_dir_entry));
+
+                        // Write sector back
+                        return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+                    }
+                }
+            } // End of if
+        }
+        else
+            break;
+    } // End of while loop
+
+    return 0;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_mark_file_deleted: Find a SFN entry and mark if as deleted
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_mark_file_deleted(struct fatfs *fs, uint32 Cluster, char *shortname)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    int x=0;
+    struct fat_dir_entry *directoryEntry;
+
+    // No write access?
+    if (!fs->disk_io.write_media)
+        return 0;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if (fatfs_entry_lfn_text(directoryEntry) )
+                    ;
+
+                // If Invalid record found delete any long file name information collated
+                else if (fatfs_entry_lfn_invalid(directoryEntry) )
+                    ;
+
+                // Normal Entry, only 8.3 Text
+                else
+#endif
+                if (fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    if (strncmp((const char *)directoryEntry->Name, shortname, 11)==0)
+                    {
+                        // Mark as deleted
+                        directoryEntry->Name[0] = FILE_HEADER_DELETED;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                        // Update access / modify time & date
+                        fatfs_update_timestamps(directoryEntry, 0, 1, 1);
+#endif
+
+                        // Update sfn entry
+                        memcpy((uint8*)(fs->currentsector.sector+recordoffset), (uint8*)directoryEntry, sizeof(struct fat_dir_entry));
+
+                        // Write sector back
+                        return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+                    }
+                }
+            } // End of if
+        }
+        else
+            break;
+    } // End of while loop
+
+    return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_list_directory_start: Initialise a directory listing procedure
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+void fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster)
+{
+    dirls->cluster = StartCluster;
+    dirls->sector = 0;
+    dirls->offset = 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_list_directory_next: Get the next entry in the directory.
+// Returns: 1 = found, 0 = end of listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fatfs_list_directory_next(struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry)
+{
+    uint8 i,item;
+    uint16 recordoffset;
+    struct fat_dir_entry *directoryEntry;
+    char *long_filename = NULL;
+    char short_filename[13];
+    struct lfn_cache lfn;
+    int dotRequired = 0;
+    int result = 0;
+
+    // Initialise LFN cache first
+    fatfs_lfn_cache_init(&lfn, 0);
+
+    while (1)
+    {
+        // If data read OK
+        if (fatfs_sector_reader(fs, dirls->cluster, dirls->sector, 0))
+        {
+            // Maximum of 16 directory entries
+            for (item = dirls->offset; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Increase directory offset
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if ( fatfs_entry_lfn_text(directoryEntry) )
+                    fatfs_lfn_cache_entry(&lfn, fs->currentsector.sector+recordoffset);
+
+                // If Invalid record found delete any long file name information collated
+                else if ( fatfs_entry_lfn_invalid(directoryEntry) )
+                    fatfs_lfn_cache_init(&lfn, 0);
+
+                // Normal SFN Entry and Long text exists
+                else if (fatfs_entry_lfn_exists(&lfn, directoryEntry) )
+                {
+                    // Get text
+                    long_filename = fatfs_lfn_cache_get(&lfn);
+                    strcpy_s(entry->filename, FATFS_MAX_LONG_FILENAME - 1, long_filename);
+
+                    if (fatfs_entry_is_dir(directoryEntry))
+                        entry->is_dir = 1;
+                    else
+                        entry->is_dir = 0;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                    // Get time / dates
+                    entry->create_time = ((uint16)directoryEntry->CrtTime[1] << 8) | directoryEntry->CrtTime[0];
+                    entry->create_date = ((uint16)directoryEntry->CrtDate[1] << 8) | directoryEntry->CrtDate[0];
+                    entry->access_date = ((uint16)directoryEntry->LstAccDate[1] << 8) | directoryEntry->LstAccDate[0];
+                    entry->write_time  = ((uint16)directoryEntry->WrtTime[1] << 8) | directoryEntry->WrtTime[0];
+                    entry->write_date  = ((uint16)directoryEntry->WrtDate[1] << 8) | directoryEntry->WrtDate[0];
+#endif
+
+                    entry->size = FAT_HTONL(directoryEntry->FileSize);
+                    entry->cluster = (FAT_HTONS(directoryEntry->FstClusHI)<<16) | FAT_HTONS(directoryEntry->FstClusLO);
+
+                    // Next starting position
+                    dirls->offset = item + 1;
+                    result = 1;
+                    return 1;
+                }
+                // Normal Entry, only 8.3 Text
+                else
+#endif
+                if ( fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    fatfs_lfn_cache_init(&lfn, 0);
+
+                    memset(short_filename, 0, sizeof(short_filename));
+
+                    // Copy name to string
+                    for (i=0; i<8; i++)
+                        short_filename[i] = directoryEntry->Name[i];
+
+                    // Extension
+                    dotRequired = 0;
+                    for (i=8; i<11; i++)
+                    {
+                        short_filename[i+1] = directoryEntry->Name[i];
+                        if (directoryEntry->Name[i] != ' ')
+                            dotRequired = 1;
+                    }
+
+                    // Dot only required if extension present
+                    if (dotRequired)
+                    {
+                        // If not . or .. entry
+                        if (short_filename[0]!='.')
+                            short_filename[8] = '.';
+                        else
+                            short_filename[8] = ' ';
+                    }
+                    else
+                        short_filename[8] = ' ';
+
+                    fatfs_get_sfn_display_name(entry->filename, short_filename);
+
+                    if (fatfs_entry_is_dir(directoryEntry))
+                        entry->is_dir = 1;
+                    else
+                        entry->is_dir = 0;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                    // Get time / dates
+                    entry->create_time = ((uint16)directoryEntry->CrtTime[1] << 8) | directoryEntry->CrtTime[0];
+                    entry->create_date = ((uint16)directoryEntry->CrtDate[1] << 8) | directoryEntry->CrtDate[0];
+                    entry->access_date = ((uint16)directoryEntry->LstAccDate[1] << 8) | directoryEntry->LstAccDate[0];
+                    entry->write_time  = ((uint16)directoryEntry->WrtTime[1] << 8) | directoryEntry->WrtTime[0];
+                    entry->write_date  = ((uint16)directoryEntry->WrtDate[1] << 8) | directoryEntry->WrtDate[0];
+#endif
+
+                    entry->size = FAT_HTONL(directoryEntry->FileSize);
+                    entry->cluster = (FAT_HTONS(directoryEntry->FstClusHI)<<16) | FAT_HTONS(directoryEntry->FstClusLO);
+
+                    // Next starting position
+                    dirls->offset = item + 1;
+                    result = 1;
+                    return 1;
+                }
+            }// end of for
+
+            // If reached end of the dir move onto next sector
+            dirls->sector++;
+            dirls->offset = 0;
+        }
+        else
+            break;
+    }
+
+    return result;
+}
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_access.h b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_access.h
new file mode 100644 (file)
index 0000000..1752387
--- /dev/null
@@ -0,0 +1,133 @@
+#ifndef __FAT_ACCESS_H__
+#define __FAT_ACCESS_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define FAT_INIT_OK                         0
+#define FAT_INIT_MEDIA_ACCESS_ERROR         (-1)
+#define FAT_INIT_INVALID_SECTOR_SIZE        (-2)
+#define FAT_INIT_INVALID_SIGNATURE          (-3)
+#define FAT_INIT_ENDIAN_ERROR               (-4)
+#define FAT_INIT_WRONG_FILESYS_TYPE         (-5)
+#define FAT_INIT_WRONG_PARTITION_TYPE       (-6)
+#define FAT_INIT_STRUCT_PACKING             (-7)
+
+#define FAT_DIR_ENTRIES_PER_SECTOR          (FAT_SECTOR_SIZE / FAT_DIR_ENTRY_SIZE)
+
+//-----------------------------------------------------------------------------
+// Function Pointers
+//-----------------------------------------------------------------------------
+typedef int (*fn_diskio_read) (uint32 sector, uint8 *buffer, uint32 sector_count);
+typedef int (*fn_diskio_write)(uint32 sector, uint8 *buffer, uint32 sector_count);
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct disk_if
+{
+    // User supplied function pointers for disk IO
+    fn_diskio_read          read_media;
+    fn_diskio_write         write_media;
+};
+
+// Forward declaration
+struct fat_buffer;
+
+struct fat_buffer
+{
+    uint8                   sector[FAT_SECTOR_SIZE * FAT_BUFFER_SECTORS];
+    uint32                  address;
+    int                     dirty;
+    uint8 *                 ptr;
+
+    // Next in chain of sector buffers
+    struct fat_buffer       *next;
+};
+
+typedef enum eFatType
+{
+    FAT_TYPE_16,
+    FAT_TYPE_32
+} tFatType;
+
+struct fatfs
+{
+    // Filesystem globals
+    uint8                   sectors_per_cluster;
+    uint32                  cluster_begin_lba;
+    uint32                  rootdir_first_cluster;
+    uint32                  rootdir_first_sector;
+    uint32                  rootdir_sectors;
+    uint32                  fat_begin_lba;
+    uint16                  fs_info_sector;
+    uint32                  lba_begin;
+    uint32                  fat_sectors;
+    uint32                  next_free_cluster;
+    uint16                  root_entry_count;
+    uint16                  reserved_sectors;
+    uint8                   num_of_fats;
+    tFatType                fat_type;
+
+    // Disk/Media API
+    struct disk_if          disk_io;
+
+    // [Optional] Thread Safety
+    void                    (*fl_lock)(void);
+    void                    (*fl_unlock)(void);
+
+    // Working buffer
+    struct fat_buffer        currentsector;
+
+    // FAT Buffer
+    struct fat_buffer        *fat_buffer_head;
+    struct fat_buffer        fat_buffers[FAT_BUFFERS];
+};
+
+struct fs_dir_list_status
+{
+    uint32                  sector;
+    uint32                  cluster;
+    uint8                   offset;
+};
+
+struct fs_dir_ent
+{
+    char                    filename[FATFS_MAX_LONG_FILENAME];
+    uint8                   is_dir;
+    uint32                  cluster;
+    uint32                  size;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+    uint16                  access_date;
+    uint16                  write_time;
+    uint16                  write_date;
+    uint16                  create_date;
+    uint16                  create_time;
+#endif
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int     fatfs_init(struct fatfs *fs);
+uint32  fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number);
+int     fatfs_sector_reader(struct fatfs *fs, uint32 Startcluster, uint32 offset, uint8 *target);
+int     fatfs_sector_read(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int     fatfs_sector_write(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int     fatfs_read_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+int     fatfs_write_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+void    fatfs_show_details(struct fatfs *fs);
+uint32  fatfs_get_root_cluster(struct fatfs *fs);
+uint32  fatfs_get_file_entry(struct fatfs *fs, uint32 Cluster, char *nametofind, struct fat_dir_entry *sfEntry);
+int     fatfs_sfn_exists(struct fatfs *fs, uint32 Cluster, char *shortname);
+int     fatfs_update_file_length(struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength);
+int     fatfs_mark_file_deleted(struct fatfs *fs, uint32 Cluster, char *shortname);
+void    fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster);
+int     fatfs_list_directory_next(struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry);
+int     fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access);
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_cache.c b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_cache.c
new file mode 100644 (file)
index 0000000..16e8494
--- /dev/null
@@ -0,0 +1,91 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//                            FAT16/32 File IO Library
+//                                    V2.6
+//                              Ultra-Embedded.com
+//                            Copyright 2003 - 2012
+//
+//                         Email: admin@ultra-embedded.com
+//
+//                                License: GPL
+//   If you would like a version with a more permissive license for use in
+//   closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include <string.h>
+#include "fat_cache.h"
+
+// Per file cluster chain caching used to improve performance.
+// This does not have to be enabled for architectures with low
+// memory space.
+
+//-----------------------------------------------------------------------------
+// fatfs_cache_init:
+//-----------------------------------------------------------------------------
+int fatfs_cache_init(struct fatfs *fs, FL_FILE *file)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+    int i;
+
+    for (i=0;i<FAT_CLUSTER_CACHE_ENTRIES;i++)
+    {
+        file->cluster_cache_idx[i] = 0xFFFFFFFF; // Not used
+        file->cluster_cache_data[i] = 0;
+    }
+#endif
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_cache_get_next_cluster:
+//-----------------------------------------------------------------------------
+int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+    uint32 slot = clusterIdx % FAT_CLUSTER_CACHE_ENTRIES;
+
+    if (file->cluster_cache_idx[slot] == clusterIdx)
+    {
+        *pNextCluster = file->cluster_cache_data[slot];
+        return 1;
+    }
+#endif
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_cache_set_next_cluster:
+//-----------------------------------------------------------------------------
+int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+    uint32 slot = clusterIdx % FAT_CLUSTER_CACHE_ENTRIES;
+
+    if (file->cluster_cache_idx[slot] == clusterIdx)
+        file->cluster_cache_data[slot] = nextCluster;
+    else
+    {
+        file->cluster_cache_idx[slot] = clusterIdx;
+        file->cluster_cache_data[slot] = nextCluster;
+    }
+#endif
+
+    return 1;
+}
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_cache.h b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_cache.h
new file mode 100644 (file)
index 0000000..348d5d3
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __FAT_CACHE_H__
+#define __FAT_CACHE_H__
+
+#include "fat_filelib.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_cache_init(struct fatfs *fs, FL_FILE *file);
+int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster);
+int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster);
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_defs.h b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_defs.h
new file mode 100644 (file)
index 0000000..5fe8d6a
--- /dev/null
@@ -0,0 +1,128 @@
+#ifndef __FAT_DEFS_H__
+#define __FAT_DEFS_H__
+
+#include "fat_opts.h"
+#include "fat_types.h"
+
+//-----------------------------------------------------------------------------
+//            FAT32 Offsets
+//        Name                Offset
+//-----------------------------------------------------------------------------
+
+// Boot Sector
+#define BS_JMPBOOT              0    // Length = 3
+#define BS_OEMNAME              3    // Length = 8
+#define BPB_BYTSPERSEC          11    // Length = 2
+#define BPB_SECPERCLUS          13    // Length = 1
+#define BPB_RSVDSECCNT          14    // Length = 2
+#define BPB_NUMFATS             16    // Length = 1
+#define BPB_ROOTENTCNT          17    // Length = 2
+#define BPB_TOTSEC16            19    // Length = 2
+#define BPB_MEDIA               21    // Length = 1
+#define    BPB_FATSZ16          22    // Length = 2
+#define BPB_SECPERTRK           24    // Length = 2
+#define BPB_NUMHEADS            26    // Length = 2
+#define BPB_HIDDSEC             28    // Length = 4
+#define BPB_TOTSEC32            32    // Length = 4
+
+// FAT 12/16
+#define BS_FAT_DRVNUM           36    // Length = 1
+#define BS_FAT_BOOTSIG          38    // Length = 1
+#define BS_FAT_VOLID            39    // Length = 4
+#define BS_FAT_VOLLAB           43    // Length = 11
+#define BS_FAT_FILSYSTYPE       54    // Length = 8
+
+// FAT 32
+#define BPB_FAT32_FATSZ32       36    // Length = 4
+#define BPB_FAT32_EXTFLAGS      40    // Length = 2
+#define BPB_FAT32_FSVER         42    // Length = 2
+#define BPB_FAT32_ROOTCLUS      44    // Length = 4
+#define BPB_FAT32_FSINFO        48    // Length = 2
+#define BPB_FAT32_BKBOOTSEC     50    // Length = 2
+#define BS_FAT32_DRVNUM         64    // Length = 1
+#define BS_FAT32_BOOTSIG        66    // Length = 1
+#define BS_FAT32_VOLID          67    // Length = 4
+#define BS_FAT32_VOLLAB         71    // Length = 11
+#define BS_FAT32_FILSYSTYPE     82    // Length = 8
+
+//-----------------------------------------------------------------------------
+// FAT Types
+//-----------------------------------------------------------------------------
+#define FAT_TYPE_FAT12          1
+#define FAT_TYPE_FAT16          2
+#define FAT_TYPE_FAT32          3
+
+//-----------------------------------------------------------------------------
+// FAT32 Specific Statics
+//-----------------------------------------------------------------------------
+#define SIGNATURE_POSITION              510
+#define SIGNATURE_VALUE                 0xAA55
+#define PARTITION1_TYPECODE_LOCATION    450
+#define FAT32_TYPECODE1                 0x0B
+#define FAT32_TYPECODE2                 0x0C
+#define PARTITION1_LBA_BEGIN_LOCATION   454
+#define PARTITION1_SIZE_LOCATION        458
+
+#define FAT_DIR_ENTRY_SIZE              32
+#define FAT_SFN_SIZE_FULL               11
+#define FAT_SFN_SIZE_PARTIAL            8
+
+//-----------------------------------------------------------------------------
+// FAT32 File Attributes and Types
+//-----------------------------------------------------------------------------
+#define FILE_ATTR_READ_ONLY             0x01
+#define FILE_ATTR_HIDDEN                0x02
+#define FILE_ATTR_SYSTEM                0x04
+#define FILE_ATTR_SYSHID                0x06
+#define FILE_ATTR_VOLUME_ID             0x08
+#define FILE_ATTR_DIRECTORY             0x10
+#define FILE_ATTR_ARCHIVE               0x20
+#define FILE_ATTR_LFN_TEXT              0x0F
+#define FILE_HEADER_BLANK               0x00
+#define FILE_HEADER_DELETED             0xE5
+#define FILE_TYPE_DIR                   0x10
+#define FILE_TYPE_FILE                  0x20
+
+//-----------------------------------------------------------------------------
+// Time / Date details
+//-----------------------------------------------------------------------------
+#define FAT_TIME_HOURS_SHIFT            11
+#define FAT_TIME_HOURS_MASK             0x1F
+#define FAT_TIME_MINUTES_SHIFT          5
+#define FAT_TIME_MINUTES_MASK           0x3F
+#define FAT_TIME_SECONDS_SHIFT          0
+#define FAT_TIME_SECONDS_MASK           0x1F
+#define FAT_TIME_SECONDS_SCALE          2
+#define FAT_DATE_YEAR_SHIFT             9
+#define FAT_DATE_YEAR_MASK              0x7F
+#define FAT_DATE_MONTH_SHIFT            5
+#define FAT_DATE_MONTH_MASK             0xF
+#define FAT_DATE_DAY_SHIFT              0
+#define FAT_DATE_DAY_MASK               0x1F
+#define FAT_DATE_YEAR_OFFSET            1980
+
+//-----------------------------------------------------------------------------
+// Other Defines
+//-----------------------------------------------------------------------------
+#define FAT32_LAST_CLUSTER              0xFFFFFFFF
+#define FAT32_INVALID_CLUSTER           0xFFFFFFFF
+
+STRUCT_PACK_BEGIN
+struct fat_dir_entry STRUCT_PACK
+{
+    uint8 Name[11];
+    uint8 Attr;
+    uint8 NTRes;
+    uint8 CrtTimeTenth;
+    uint8 CrtTime[2];
+    uint8 CrtDate[2];
+    uint8 LstAccDate[2];
+    uint16 FstClusHI;
+    uint8 WrtTime[2];
+    uint8 WrtDate[2];
+    uint16 FstClusLO;
+    uint32 FileSize;
+} STRUCT_PACKED;
+STRUCT_PACK_END
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_filelib.c b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_filelib.c
new file mode 100644 (file)
index 0000000..567ac55
--- /dev/null
@@ -0,0 +1,1603 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//                            FAT16/32 File IO Library
+//                                    V2.6
+//                              Ultra-Embedded.com
+//                            Copyright 2003 - 2012
+//
+//                         Email: admin@ultra-embedded.com
+//
+//                                License: GPL
+//   If you would like a version with a more permissive license for use in
+//   closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include <stdlib.h>
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_misc.h"
+#include "fat_string.h"
+#include "fat_filelib.h"
+#include "fat_cache.h"
+
+//-----------------------------------------------------------------------------
+// Locals
+//-----------------------------------------------------------------------------
+static FL_FILE            _files[FATFS_MAX_OPEN_FILES];
+static int                _filelib_init = 0;
+static int                _filelib_valid = 0;
+static struct fatfs       _fs;
+static struct fat_list    _open_file_list;
+static struct fat_list    _free_file_list;
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+
+// Macro for checking if file lib is initialised
+#define CHECK_FL_INIT()     { if (_filelib_init==0) fl_init(); }
+
+#define FL_LOCK(a)          do { if ((a)->fl_lock) (a)->fl_lock(); } while (0)
+#define FL_UNLOCK(a)        do { if ((a)->fl_unlock) (a)->fl_unlock(); } while (0)
+
+//-----------------------------------------------------------------------------
+// Local Functions
+//-----------------------------------------------------------------------------
+static void                _fl_init();
+
+//-----------------------------------------------------------------------------
+// _allocate_file: Find a slot in the open files buffer for a new file
+//-----------------------------------------------------------------------------
+static FL_FILE* _allocate_file(void)
+{
+    // Allocate free file
+    struct fat_node *node = fat_list_pop_head(&_free_file_list);
+
+    // Add to open list
+    if (node)
+        fat_list_insert_last(&_open_file_list, node);
+
+    return fat_list_entry(node, FL_FILE, list_node);
+}
+//-----------------------------------------------------------------------------
+// _check_file_open: Returns true if the file is already open
+//-----------------------------------------------------------------------------
+static int _check_file_open(FL_FILE* file)
+{
+    struct fat_node *node;
+
+    // Compare open files
+    fat_list_for_each(&_open_file_list, node)
+    {
+        FL_FILE* openFile = fat_list_entry(node, FL_FILE, list_node);
+
+        // If not the current file
+        if (openFile != file)
+        {
+            // Compare path and name
+            if ( (fatfs_compare_names(openFile->path,file->path)) && (fatfs_compare_names(openFile->filename,file->filename)) )
+                return 1;
+        }
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// _free_file: Free open file handle
+//-----------------------------------------------------------------------------
+static void _free_file(FL_FILE* file)
+{
+    // Remove from open list
+    fat_list_remove(&_open_file_list, &file->list_node);
+
+    // Add to free list
+    fat_list_insert_last(&_free_file_list, &file->list_node);
+}
+
+//-----------------------------------------------------------------------------
+//                                Low Level
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// _open_directory: Cycle through path string to find the start cluster
+// address of the highest subdir.
+//-----------------------------------------------------------------------------
+static int _open_directory(char *path, uint32 *pathCluster)
+{
+    int levels;
+    int sublevel;
+    char currentfolder[FATFS_MAX_LONG_FILENAME];
+    struct fat_dir_entry sfEntry;
+    uint32 startcluster;
+
+    // Set starting cluster to root cluster
+    startcluster = fatfs_get_root_cluster(&_fs);
+
+    // Find number of levels
+    levels = fatfs_total_path_levels(path);
+
+    // Cycle through each level and get the start sector
+    for (sublevel=0;sublevel<(levels+1);sublevel++)
+    {
+        if (fatfs_get_substring(path, sublevel, currentfolder, sizeof(currentfolder)) == -1)
+            return 0;
+
+        // Find clusteraddress for folder (currentfolder)
+        if (fatfs_get_file_entry(&_fs, startcluster, currentfolder,&sfEntry))
+        {
+            // Check entry is folder
+            if (fatfs_entry_is_dir(&sfEntry))
+                startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
+            else
+                return 0;
+        }
+        else
+            return 0;
+    }
+
+    *pathCluster = startcluster;
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// _create_directory: Cycle through path string and create the end directory
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static int _create_directory(char *path)
+{
+    FL_FILE* file;
+    struct fat_dir_entry sfEntry;
+    char shortFilename[FAT_SFN_SIZE_FULL];
+    int tailNum = 0;
+    int i;
+
+    // Allocate a new file handle
+    file = _allocate_file();
+    if (!file)
+        return 0;
+
+    // Clear filename
+    memset(file->path, '\0', sizeof(file->path));
+    memset(file->filename, '\0', sizeof(file->filename));
+
+    // Split full path into filename and directory path
+    if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+    {
+        _free_file(file);
+        return 0;
+    }
+
+    // Check if file already open
+    if (_check_file_open(file))
+    {
+        _free_file(file);
+        return 0;
+    }
+
+    // If file is in the root dir
+    if (file->path[0] == 0)
+        file->parentcluster = fatfs_get_root_cluster(&_fs);
+    else
+    {
+        // Find parent directory start cluster
+        if (!_open_directory(file->path, &file->parentcluster))
+        {
+            _free_file(file);
+            return 0;
+        }
+    }
+
+    // Check if same filename exists in directory
+    if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1)
+    {
+        _free_file(file);
+        return 0;
+    }
+
+    file->startcluster = 0;
+
+    // Create the file space for the folder (at least one clusters worth!)
+    if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1))
+    {
+        _free_file(file);
+        return 0;
+    }
+
+    // Erase new directory cluster
+    memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
+    for (i=0;i<_fs.sectors_per_cluster;i++)
+    {
+        if (!fatfs_write_sector(&_fs, file->startcluster, i, file->file_data_sector))
+        {
+            _free_file(file);
+            return 0;
+        }
+    }
+
+#if FATFS_INC_LFN_SUPPORT
+
+    // Generate a short filename & tail
+    tailNum = 0;
+    do
+    {
+        // Create a standard short filename (without tail)
+        fatfs_lfn_create_sfn(shortFilename, file->filename);
+
+        // If second hit or more, generate a ~n tail
+        if (tailNum != 0)
+            fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
+        // Try with no tail if first entry
+        else
+            memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+        // Check if entry exists already or not
+        if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0)
+            break;
+
+        tailNum++;
+    }
+    while (tailNum < 9999);
+
+    // We reached the max number of duplicate short file names (unlikely!)
+    if (tailNum == 9999)
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return 0;
+    }
+#else
+    // Create a standard short filename (without tail)
+    if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return 0;
+    }
+
+    // Copy to SFN space
+    memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+    // Check if entry exists already
+    if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return 0;
+    }
+#endif
+
+    // Add file to disk
+    if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 1))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return 0;
+    }
+
+    // General
+    file->filelength = 0;
+    file->bytenum = 0;
+    file->file_data_address = 0xFFFFFFFF;
+    file->file_data_dirty = 0;
+    file->filelength_changed = 0;
+
+    // Quick lookup for next link in the chain
+    file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+    file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+    fatfs_fat_purge(&_fs);
+
+    _free_file(file);
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// _open_file: Open a file for reading
+//-----------------------------------------------------------------------------
+static FL_FILE* _open_file(const char *path)
+{
+    FL_FILE* file;
+    struct fat_dir_entry sfEntry;
+
+    // Allocate a new file handle
+    file = _allocate_file();
+    if (!file)
+        return NULL;
+
+    // Clear filename
+    memset(file->path, '\0', sizeof(file->path));
+    memset(file->filename, '\0', sizeof(file->filename));
+
+    // Split full path into filename and directory path
+    if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    // Check if file already open
+    if (_check_file_open(file))
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    // If file is in the root dir
+    if (file->path[0]==0)
+        file->parentcluster = fatfs_get_root_cluster(&_fs);
+    else
+    {
+        // Find parent directory start cluster
+        if (!_open_directory(file->path, &file->parentcluster))
+        {
+            _free_file(file);
+            return NULL;
+        }
+    }
+
+    // Using dir cluster address search for filename
+    if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry))
+        // Make sure entry is file not dir!
+        if (fatfs_entry_is_file(&sfEntry))
+        {
+            // Initialise file details
+            memcpy(file->shortfilename, sfEntry.Name, FAT_SFN_SIZE_FULL);
+            file->filelength = FAT_HTONL(sfEntry.FileSize);
+            file->bytenum = 0;
+            file->startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
+            file->file_data_address = 0xFFFFFFFF;
+            file->file_data_dirty = 0;
+            file->filelength_changed = 0;
+
+            // Quick lookup for next link in the chain
+            file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+            file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+            fatfs_cache_init(&_fs, file);
+
+            fatfs_fat_purge(&_fs);
+
+            return file;
+        }
+
+    _free_file(file);
+    return NULL;
+}
+//-----------------------------------------------------------------------------
+// _create_file: Create a new file
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static FL_FILE* _create_file(const char *filename)
+{
+    FL_FILE* file;
+    struct fat_dir_entry sfEntry;
+    char shortFilename[FAT_SFN_SIZE_FULL];
+    int tailNum = 0;
+
+    // No write access?
+    if (!_fs.disk_io.write_media)
+        return NULL;
+
+    // Allocate a new file handle
+    file = _allocate_file();
+    if (!file)
+        return NULL;
+
+    // Clear filename
+    memset(file->path, '\0', sizeof(file->path));
+    memset(file->filename, '\0', sizeof(file->filename));
+
+    // Split full path into filename and directory path
+    if (fatfs_split_path((char*)filename, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    // Check if file already open
+    if (_check_file_open(file))
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    // If file is in the root dir
+    if (file->path[0] == 0)
+        file->parentcluster = fatfs_get_root_cluster(&_fs);
+    else
+    {
+        // Find parent directory start cluster
+        if (!_open_directory(file->path, &file->parentcluster))
+        {
+            _free_file(file);
+            return NULL;
+        }
+    }
+
+    // Check if same filename exists in directory
+    if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1)
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    file->startcluster = 0;
+
+    // Create the file space for the file (at least one clusters worth!)
+    if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1))
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+#if FATFS_INC_LFN_SUPPORT
+    // Generate a short filename & tail
+    tailNum = 0;
+    do
+    {
+        // Create a standard short filename (without tail)
+        fatfs_lfn_create_sfn(shortFilename, file->filename);
+
+        // If second hit or more, generate a ~n tail
+        if (tailNum != 0)
+            fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
+        // Try with no tail if first entry
+        else
+            memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+        // Check if entry exists already or not
+        if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0)
+            break;
+
+        tailNum++;
+    }
+    while (tailNum < 9999);
+
+    // We reached the max number of duplicate short file names (unlikely!)
+    if (tailNum == 9999)
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return NULL;
+    }
+#else
+    // Create a standard short filename (without tail)
+    if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return NULL;
+    }
+
+    // Copy to SFN space
+    memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+    // Check if entry exists already
+    if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return NULL;
+    }
+#endif
+
+    // Add file to disk
+    if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 0))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return NULL;
+    }
+
+    // General
+    file->filelength = 0;
+    file->bytenum = 0;
+    file->file_data_address = 0xFFFFFFFF;
+    file->file_data_dirty = 0;
+    file->filelength_changed = 0;
+
+    // Quick lookup for next link in the chain
+    file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+    file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+    fatfs_cache_init(&_fs, file);
+
+    fatfs_fat_purge(&_fs);
+
+    return file;
+}
+#endif
+//-----------------------------------------------------------------------------
+// _read_sectors: Read sector(s) from disk to file
+//-----------------------------------------------------------------------------
+static uint32 _read_sectors(FL_FILE* file, uint32 offset, uint8 *buffer, uint32 count)
+{
+    uint32 Sector = 0;
+    uint32 ClusterIdx = 0;
+    uint32 Cluster = 0;
+    uint32 i;
+    uint32 lba;
+
+    // Find cluster index within file & sector with cluster
+    ClusterIdx = offset / _fs.sectors_per_cluster;
+    Sector = offset - (ClusterIdx * _fs.sectors_per_cluster);
+
+    // Limit number of sectors read to the number remaining in this cluster
+    if ((Sector + count) > _fs.sectors_per_cluster)
+        count = _fs.sectors_per_cluster - Sector;
+
+    // Quick lookup for next link in the chain
+    if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
+        Cluster = file->last_fat_lookup.CurrentCluster;
+    // Else walk the chain
+    else
+    {
+        // Starting from last recorded cluster?
+        if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
+        {
+            i = file->last_fat_lookup.ClusterIdx;
+            Cluster = file->last_fat_lookup.CurrentCluster;
+        }
+        // Start searching from the beginning..
+        else
+        {
+            // Set start of cluster chain to initial value
+            i = 0;
+            Cluster = file->startcluster;
+        }
+
+        // Follow chain to find cluster to read
+        for ( ;i<ClusterIdx; i++)
+        {
+            uint32 nextCluster;
+
+            // Does the entry exist in the cache?
+            if (!fatfs_cache_get_next_cluster(&_fs, file, i, &nextCluster))
+            {
+                // Scan file linked list to find next entry
+                nextCluster = fatfs_find_next_cluster(&_fs, Cluster);
+
+                // Push entry into cache
+                fatfs_cache_set_next_cluster(&_fs, file, i, nextCluster);
+            }
+
+            Cluster = nextCluster;
+        }
+
+        // Record current cluster lookup details (if valid)
+        if (Cluster != FAT32_LAST_CLUSTER)
+        {
+            file->last_fat_lookup.CurrentCluster = Cluster;
+            file->last_fat_lookup.ClusterIdx = ClusterIdx;
+        }
+    }
+
+    // If end of cluster chain then return false
+    if (Cluster == FAT32_LAST_CLUSTER)
+        return 0;
+
+    // Calculate sector address
+    lba = fatfs_lba_of_cluster(&_fs, Cluster) + Sector;
+
+    // Read sector of file
+    if (fatfs_sector_read(&_fs, lba, buffer, count))
+        return count;
+    else
+        return 0;
+}
+
+//-----------------------------------------------------------------------------
+//                                External API
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// fl_init: Initialise library
+//-----------------------------------------------------------------------------
+void fl_init(void)
+{
+    int i;
+
+    fat_list_init(&_free_file_list);
+    fat_list_init(&_open_file_list);
+
+    // Add all file objects to free list
+    for (i=0;i<FATFS_MAX_OPEN_FILES;i++)
+        fat_list_insert_last(&_free_file_list, &_files[i].list_node);
+
+    _filelib_init = 1;
+}
+//-----------------------------------------------------------------------------
+// fl_attach_locks:
+//-----------------------------------------------------------------------------
+void fl_attach_locks(void (*lock)(void), void (*unlock)(void))
+{
+    _fs.fl_lock = lock;
+    _fs.fl_unlock = unlock;
+}
+//-----------------------------------------------------------------------------
+// fl_attach_media:
+//-----------------------------------------------------------------------------
+int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)
+{
+    int res;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    _fs.disk_io.read_media = rd;
+    _fs.disk_io.write_media = wr;
+
+    // Initialise FAT parameters
+    if ((res = fatfs_init(&_fs)) != FAT_INIT_OK)
+    {
+        FAT_PRINTF(("FAT_FS: Error could not load FAT details (%d)!\r\n", res));
+        return res;
+    }
+
+    _filelib_valid = 1;
+    return FAT_INIT_OK;
+}
+//-----------------------------------------------------------------------------
+// fl_shutdown: Call before shutting down system
+//-----------------------------------------------------------------------------
+void fl_shutdown(void)
+{
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+    fatfs_fat_purge(&_fs);
+    FL_UNLOCK(&_fs);
+}
+//-----------------------------------------------------------------------------
+// fopen: Open or Create a file for reading or writing
+//-----------------------------------------------------------------------------
+void* fl_fopen(const char *path, const char *mode)
+{
+    int i;
+    FL_FILE* file;
+    uint8 flags = 0;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (!_filelib_valid)
+        return NULL;
+
+    if (!path || !mode)
+        return NULL;
+
+    // Supported Modes:
+    // "r" Open a file for reading.
+    //        The file must exist.
+    // "w" Create an empty file for writing.
+    //        If a file with the same name already exists its content is erased and the file is treated as a new empty file.
+    // "a" Append to a file.
+    //        Writing operations append data at the end of the file.
+    //        The file is created if it does not exist.
+    // "r+" Open a file for update both reading and writing.
+    //        The file must exist.
+    // "w+" Create an empty file for both reading and writing.
+    //        If a file with the same name already exists its content is erased and the file is treated as a new empty file.
+    // "a+" Open a file for reading and appending.
+    //        All writing operations are performed at the end of the file, protecting the previous content to be overwritten.
+    //        You can reposition (fseek, rewind) the internal pointer to anywhere in the file for reading, but writing operations
+    //        will move it back to the end of file.
+    //        The file is created if it does not exist.
+
+    for (i=0;i<(int)strlen(mode);i++)
+    {
+        switch (mode[i])
+        {
+        case 'r':
+        case 'R':
+            flags |= FILE_READ;
+            break;
+        case 'w':
+        case 'W':
+            flags |= FILE_WRITE;
+            flags |= FILE_ERASE;
+            flags |= FILE_CREATE;
+            break;
+        case 'a':
+        case 'A':
+            flags |= FILE_WRITE;
+            flags |= FILE_APPEND;
+            flags |= FILE_CREATE;
+            break;
+        case '+':
+            if (flags & FILE_READ)
+                flags |= FILE_WRITE;
+            else if (flags & FILE_WRITE)
+            {
+                flags |= FILE_READ;
+                flags |= FILE_ERASE;
+                flags |= FILE_CREATE;
+            }
+            else if (flags & FILE_APPEND)
+            {
+                flags |= FILE_READ;
+                flags |= FILE_WRITE;
+                flags |= FILE_APPEND;
+                flags |= FILE_CREATE;
+            }
+            break;
+        case 'b':
+        case 'B':
+            flags |= FILE_BINARY;
+            break;
+        }
+    }
+
+    file = NULL;
+
+#if FATFS_INC_WRITE_SUPPORT == 0
+    // No write support!
+    flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
+#endif
+
+    // No write access - remove write/modify flags
+    if (!_fs.disk_io.write_media)
+        flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
+
+    FL_LOCK(&_fs);
+
+    // Read
+    if (flags & FILE_READ)
+        file = _open_file(path);
+
+    // Create New
+#if FATFS_INC_WRITE_SUPPORT
+    if (!file && (flags & FILE_CREATE))
+        file = _create_file(path);
+#endif
+
+    // Write Existing (and not open due to read or create)
+    if (!(flags & FILE_READ))
+        if ((flags & FILE_CREATE) && !file)
+            if (flags & (FILE_WRITE | FILE_APPEND))
+                file = _open_file(path);
+
+    if (file)
+        file->flags = flags;
+
+    FL_UNLOCK(&_fs);
+    return file;
+}
+//-----------------------------------------------------------------------------
+// _write_sectors: Write sector(s) to disk
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static uint32 _write_sectors(FL_FILE* file, uint32 offset, uint8 *buf, uint32 count)
+{
+    uint32 SectorNumber = 0;
+    uint32 ClusterIdx = 0;
+    uint32 Cluster = 0;
+    uint32 LastCluster = FAT32_LAST_CLUSTER;
+    uint32 i;
+    uint32 lba;
+    uint32 TotalWriteCount = count;
+
+    // Find values for Cluster index & sector within cluster
+    ClusterIdx = offset / _fs.sectors_per_cluster;
+    SectorNumber = offset - (ClusterIdx * _fs.sectors_per_cluster);
+
+    // Limit number of sectors written to the number remaining in this cluster
+    if ((SectorNumber + count) > _fs.sectors_per_cluster)
+        count = _fs.sectors_per_cluster - SectorNumber;
+
+    // Quick lookup for next link in the chain
+    if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
+        Cluster = file->last_fat_lookup.CurrentCluster;
+    // Else walk the chain
+    else
+    {
+        // Starting from last recorded cluster?
+        if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
+        {
+            i = file->last_fat_lookup.ClusterIdx;
+            Cluster = file->last_fat_lookup.CurrentCluster;
+        }
+        // Start searching from the beginning..
+        else
+        {
+            // Set start of cluster chain to initial value
+            i = 0;
+            Cluster = file->startcluster;
+        }
+
+        // Follow chain to find cluster to read
+        for ( ;i<ClusterIdx; i++)
+        {
+            uint32 nextCluster;
+
+            // Does the entry exist in the cache?
+            if (!fatfs_cache_get_next_cluster(&_fs, file, i, &nextCluster))
+            {
+                // Scan file linked list to find next entry
+                nextCluster = fatfs_find_next_cluster(&_fs, Cluster);
+
+                // Push entry into cache
+                fatfs_cache_set_next_cluster(&_fs, file, i, nextCluster);
+            }
+
+            LastCluster = Cluster;
+            Cluster = nextCluster;
+
+            // Dont keep following a dead end
+            if (Cluster == FAT32_LAST_CLUSTER)
+                break;
+        }
+
+        // If we have reached the end of the chain, allocate more!
+        if (Cluster == FAT32_LAST_CLUSTER)
+        {
+            // Add some more cluster(s) to the last good cluster chain
+            if (!fatfs_add_free_space(&_fs, &LastCluster,  (TotalWriteCount + _fs.sectors_per_cluster -1) / _fs.sectors_per_cluster))
+                return 0;
+
+            Cluster = LastCluster;
+        }
+
+        // Record current cluster lookup details
+        file->last_fat_lookup.CurrentCluster = Cluster;
+        file->last_fat_lookup.ClusterIdx = ClusterIdx;
+    }
+
+    // Calculate write address
+    lba = fatfs_lba_of_cluster(&_fs, Cluster) + SectorNumber;
+
+    if (fatfs_sector_write(&_fs, lba, buf, count))
+        return count;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fflush: Flush un-written data to the file
+//-----------------------------------------------------------------------------
+int fl_fflush(void *f)
+{
+#if FATFS_INC_WRITE_SUPPORT
+    FL_FILE *file = (FL_FILE *)f;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (file)
+    {
+        FL_LOCK(&_fs);
+
+        // If some write data still in buffer
+        if (file->file_data_dirty)
+        {
+            // Write back current sector before loading next
+            if (_write_sectors(file, file->file_data_address, file->file_data_sector, 1))
+                file->file_data_dirty = 0;
+        }
+
+        FL_UNLOCK(&_fs);
+    }
+#endif
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fl_fclose: Close an open file
+//-----------------------------------------------------------------------------
+void fl_fclose(void *f)
+{
+    FL_FILE *file = (FL_FILE *)f;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (file)
+    {
+        FL_LOCK(&_fs);
+
+        // Flush un-written data to file
+        fl_fflush(f);
+
+        // File size changed?
+        if (file->filelength_changed)
+        {
+#if FATFS_INC_WRITE_SUPPORT
+            // Update filesize in directory
+            fatfs_update_file_length(&_fs, file->parentcluster, (char*)file->shortfilename, file->filelength);
+#endif
+            file->filelength_changed = 0;
+        }
+
+        file->bytenum = 0;
+        file->filelength = 0;
+        file->startcluster = 0;
+        file->file_data_address = 0xFFFFFFFF;
+        file->file_data_dirty = 0;
+        file->filelength_changed = 0;
+
+        // Free file handle
+        _free_file(file);
+
+        fatfs_fat_purge(&_fs);
+
+        FL_UNLOCK(&_fs);
+    }
+}
+//-----------------------------------------------------------------------------
+// fl_fgetc: Get a character in the stream
+//-----------------------------------------------------------------------------
+int fl_fgetc(void *f)
+{
+    int res;
+    uint8 data = 0;
+
+    res = fl_fread(&data, 1, 1, f);
+    if (res == 1)
+        return (int)data;
+    else
+        return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fgets: Get a string from a stream
+//-----------------------------------------------------------------------------
+char *fl_fgets(char *s, int n, void *f)
+{
+    int idx = 0;
+
+    // Space for null terminator?
+    if (n > 0)
+    {
+        // While space (+space for null terminator)
+        while (idx < (n-1))
+        {
+            int ch = fl_fgetc(f);
+
+            // EOF / Error?
+            if (ch < 0)
+                break;
+
+            // Store character read from stream
+            s[idx++] = (char)ch;
+
+            // End of line?
+            if (ch == '\n')
+                break;
+        }
+
+        if (idx > 0)
+            s[idx] = '\0';
+    }
+
+    return (idx > 0) ? s : 0;
+}
+//-----------------------------------------------------------------------------
+// fl_fread: Read a block of data from the file
+//-----------------------------------------------------------------------------
+int fl_fread(void * buffer, int size, int length, void *f )
+{
+    uint32 sector;
+    uint32 offset;
+    int copyCount;
+    int count = size * length;
+    int bytesRead = 0;
+
+    FL_FILE *file = (FL_FILE *)f;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (buffer==NULL || file==NULL)
+        return -1;
+
+    // No read permissions
+    if (!(file->flags & FILE_READ))
+        return -1;
+
+    // Nothing to be done
+    if (!count)
+        return 0;
+
+    // Check if read starts past end of file
+    if (file->bytenum >= file->filelength)
+        return -1;
+
+    // Limit to file size
+    if ( (file->bytenum + count) > file->filelength )
+        count = file->filelength - file->bytenum;
+
+    // Calculate start sector
+    sector = file->bytenum / FAT_SECTOR_SIZE;
+
+    // Offset to start copying data from first sector
+    offset = file->bytenum % FAT_SECTOR_SIZE;
+
+    while (bytesRead < count)
+    {
+        // Read whole sector, read from media directly into target buffer
+        if ((offset == 0) && ((count - bytesRead) >= FAT_SECTOR_SIZE))
+        {
+            // Read as many sectors as possible into target buffer
+            uint32 sectorsRead = _read_sectors(file, sector, (uint8*)((uint8*)buffer + bytesRead), (count - bytesRead) / FAT_SECTOR_SIZE);
+            if (sectorsRead)
+            {
+                // We have upto one sector to copy
+                copyCount = FAT_SECTOR_SIZE * sectorsRead;
+
+                // Move onto next sector and reset copy offset
+                sector+= sectorsRead;
+                offset = 0;
+            }
+            else
+                break;
+        }
+        else
+        {
+            // Do we need to re-read the sector?
+            if (file->file_data_address != sector)
+            {
+                // Flush un-written data to file
+                if (file->file_data_dirty)
+                    fl_fflush(file);
+
+                // Get LBA of sector offset within file
+                if (!_read_sectors(file, sector, file->file_data_sector, 1))
+                    // Read failed - out of range (probably)
+                    break;
+
+                file->file_data_address = sector;
+                file->file_data_dirty = 0;
+            }
+
+            // We have upto one sector to copy
+            copyCount = FAT_SECTOR_SIZE - offset;
+
+            // Only require some of this sector?
+            if (copyCount > (count - bytesRead))
+                copyCount = (count - bytesRead);
+
+            // Copy to application buffer
+            memcpy( (uint8*)((uint8*)buffer + bytesRead), (uint8*)(file->file_data_sector + offset), copyCount);
+
+            // Move onto next sector and reset copy offset
+            sector++;
+            offset = 0;
+        }
+
+        // Increase total read count
+        bytesRead += copyCount;
+
+        // Increment file pointer
+        file->bytenum += copyCount;
+    }
+
+    return bytesRead;
+}
+//-----------------------------------------------------------------------------
+// fl_fseek: Seek to a specific place in the file
+//-----------------------------------------------------------------------------
+int fl_fseek( void *f, long offset, int origin )
+{
+    FL_FILE *file = (FL_FILE *)f;
+    int res = -1;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (!file)
+        return -1;
+
+    if (origin == SEEK_END && offset != 0)
+        return -1;
+
+    FL_LOCK(&_fs);
+
+    // Invalidate file buffer
+    file->file_data_address = 0xFFFFFFFF;
+    file->file_data_dirty = 0;
+
+    if (origin == SEEK_SET)
+    {
+        file->bytenum = (uint32)offset;
+
+        if (file->bytenum > file->filelength)
+            file->bytenum = file->filelength;
+
+        res = 0;
+    }
+    else if (origin == SEEK_CUR)
+    {
+        // Positive shift
+        if (offset >= 0)
+        {
+            file->bytenum += offset;
+
+            if (file->bytenum > file->filelength)
+                file->bytenum = file->filelength;
+        }
+        // Negative shift
+        else
+        {
+            // Make shift positive
+            offset = -offset;
+
+            // Limit to negative shift to start of file
+            if ((uint32)offset > file->bytenum)
+                file->bytenum = 0;
+            else
+                file->bytenum-= offset;
+        }
+
+        res = 0;
+    }
+    else if (origin == SEEK_END)
+    {
+        file->bytenum = file->filelength;
+        res = 0;
+    }
+    else
+        res = -1;
+
+    FL_UNLOCK(&_fs);
+
+    return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fgetpos: Get the current file position
+//-----------------------------------------------------------------------------
+int fl_fgetpos(void *f , uint32 * position)
+{
+    FL_FILE *file = (FL_FILE *)f;
+
+    if (!file)
+        return -1;
+
+    FL_LOCK(&_fs);
+
+    // Get position
+    *position = file->bytenum;
+
+    FL_UNLOCK(&_fs);
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fl_ftell: Get the current file position
+//-----------------------------------------------------------------------------
+long fl_ftell(void *f)
+{
+    uint32 pos = 0;
+
+    fl_fgetpos(f, &pos);
+
+    return (long)pos;
+}
+//-----------------------------------------------------------------------------
+// fl_feof: Is the file pointer at the end of the stream?
+//-----------------------------------------------------------------------------
+int fl_feof(void *f)
+{
+    FL_FILE *file = (FL_FILE *)f;
+    int res;
+
+    if (!file)
+        return -1;
+
+    FL_LOCK(&_fs);
+
+    if (file->bytenum == file->filelength)
+        res = EOF;
+    else
+        res = 0;
+
+    FL_UNLOCK(&_fs);
+
+    return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fputc: Write a character to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fputc(int c, void *f)
+{
+    uint8 data = (uint8)c;
+    int res;
+
+    res = fl_fwrite(&data, 1, 1, f);
+    if (res == 1)
+        return c;
+    else
+        return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fwrite: Write a block of data to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fwrite(const void * data, int size, int count, void *f )
+{
+    FL_FILE *file = (FL_FILE *)f;
+    uint32 sector;
+    uint32 offset;
+    uint32 length = (size*count);
+    uint8 *buffer = (uint8 *)data;
+    uint32 bytesWritten = 0;
+    uint32 copyCount;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (!file)
+        return -1;
+
+    FL_LOCK(&_fs);
+
+    // No write permissions
+    if (!(file->flags & FILE_WRITE))
+    {
+        FL_UNLOCK(&_fs);
+        return -1;
+    }
+
+    // Append writes to end of file
+    if (file->flags & FILE_APPEND)
+        file->bytenum = file->filelength;
+    // Else write to current position
+
+    // Calculate start sector
+    sector = file->bytenum / FAT_SECTOR_SIZE;
+
+    // Offset to start copying data from first sector
+    offset = file->bytenum % FAT_SECTOR_SIZE;
+
+    while (bytesWritten < length)
+    {
+        // Whole sector or more to be written?
+        if ((offset == 0) && ((length - bytesWritten) >= FAT_SECTOR_SIZE))
+        {
+            uint32 sectorsWrote;
+
+            // Buffered sector, flush back to disk
+            if (file->file_data_address != 0xFFFFFFFF)
+            {
+                // Flush un-written data to file
+                if (file->file_data_dirty)
+                    fl_fflush(file);
+
+                file->file_data_address = 0xFFFFFFFF;
+                file->file_data_dirty = 0;
+            }
+
+            // Write as many sectors as possible
+            sectorsWrote = _write_sectors(file, sector, (uint8*)(buffer + bytesWritten), (length - bytesWritten) / FAT_SECTOR_SIZE);
+            copyCount = FAT_SECTOR_SIZE * sectorsWrote;
+
+            // Increase total read count
+            bytesWritten += copyCount;
+
+            // Increment file pointer
+            file->bytenum += copyCount;
+
+            // Move onto next sector and reset copy offset
+            sector+= sectorsWrote;
+            offset = 0;
+
+            if (!sectorsWrote)
+                break;
+        }
+        else
+        {
+            // We have upto one sector to copy
+            copyCount = FAT_SECTOR_SIZE - offset;
+
+            // Only require some of this sector?
+            if (copyCount > (length - bytesWritten))
+                copyCount = (length - bytesWritten);
+
+            // Do we need to read a new sector?
+            if (file->file_data_address != sector)
+            {
+                // Flush un-written data to file
+                if (file->file_data_dirty)
+                    fl_fflush(file);
+
+                // If we plan to overwrite the whole sector, we don't need to read it first!
+                if (copyCount != FAT_SECTOR_SIZE)
+                {
+                    // NOTE: This does not have succeed; if last sector of file
+                    // reached, no valid data will be read in, but write will
+                    // allocate some more space for new data.
+
+                    // Get LBA of sector offset within file
+                    if (!_read_sectors(file, sector, file->file_data_sector, 1))
+                        memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
+                }
+
+                file->file_data_address = sector;
+                file->file_data_dirty = 0;
+            }
+
+            // Copy from application buffer into sector buffer
+            memcpy((uint8*)(file->file_data_sector + offset), (uint8*)(buffer + bytesWritten), copyCount);
+
+            // Mark buffer as dirty
+            file->file_data_dirty = 1;
+
+            // Increase total read count
+            bytesWritten += copyCount;
+
+            // Increment file pointer
+            file->bytenum += copyCount;
+
+            // Move onto next sector and reset copy offset
+            sector++;
+            offset = 0;
+        }
+    }
+
+    // Write increased extent of the file?
+    if (file->bytenum > file->filelength)
+    {
+        // Increase file size to new point
+        file->filelength = file->bytenum;
+
+        // We are changing the file length and this
+        // will need to be writen back at some point
+        file->filelength_changed = 1;
+    }
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+    // If time & date support is enabled, always force directory entry to be
+    // written in-order to update file modify / access time & date.
+    file->filelength_changed = 1;
+#endif
+
+    FL_UNLOCK(&_fs);
+
+    return (size*count);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fputs: Write a character string to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fputs(const char * str, void *f)
+{
+    int len = (int)strlen(str);
+    int res = fl_fwrite(str, 1, len, f);
+
+    if (res == len)
+        return len;
+    else
+        return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_remove: Remove a file from the filesystem
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_remove( const char * filename )
+{
+    FL_FILE* file;
+    int res = -1;
+
+    FL_LOCK(&_fs);
+
+    // Use read_file as this will check if the file is already open!
+    file = fl_fopen((char*)filename, "r");
+    if (file)
+    {
+        // Delete allocated space
+        if (fatfs_free_cluster_chain(&_fs, file->startcluster))
+        {
+            // Remove directory entries
+            if (fatfs_mark_file_deleted(&_fs, file->parentcluster, (char*)file->shortfilename))
+            {
+                // Close the file handle (this should not write anything to the file
+                // as we have not changed the file since opening it!)
+                fl_fclose(file);
+
+                res = 0;
+            }
+        }
+    }
+
+    FL_UNLOCK(&_fs);
+
+    return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_createdirectory: Create a directory based on a path
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_createdirectory(const char *path)
+{
+    int res;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+    res =_create_directory((char*)path);
+    FL_UNLOCK(&_fs);
+
+    return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_listdirectory: List a directory based on a path
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+void fl_listdirectory(const char *path)
+{
+    FL_DIR dirstat;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+
+    FAT_PRINTF(("\r\nDirectory %s\r\n", path));
+
+    if (fl_opendir(path, &dirstat))
+    {
+        struct fs_dir_ent dirent;
+
+        while (fl_readdir(&dirstat, &dirent) == 0)
+        {
+#if FATFS_INC_TIME_DATE_SUPPORT
+            int d,m,y,h,mn,s;
+            fatfs_convert_from_fat_time(dirent.write_time, &h,&m,&s);
+            fatfs_convert_from_fat_date(dirent.write_date, &d,&mn,&y);
+            FAT_PRINTF(("%02d/%02d/%04d  %02d:%02d      ", d,mn,y,h,m));
+#endif
+
+            if (dirent.is_dir)
+            {
+                FAT_PRINTF(("%s <DIR>\r\n", dirent.filename));
+            }
+            else
+            {
+                FAT_PRINTF(("%s [%d bytes]\r\n", dirent.filename, dirent.size));
+            }
+        }
+
+        fl_closedir(&dirstat);
+    }
+
+    FL_UNLOCK(&_fs);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_opendir: Opens a directory for listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+FL_DIR* fl_opendir(const char* path, FL_DIR *dir)
+{
+    int levels;
+    int res = 1;
+    uint32 cluster = FAT32_INVALID_CLUSTER;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+
+    levels = fatfs_total_path_levels((char*)path) + 1;
+
+    // If path is in the root dir
+    if (levels == 0)
+        cluster = fatfs_get_root_cluster(&_fs);
+    // Find parent directory start cluster
+    else
+        res = _open_directory((char*)path, &cluster);
+
+    if (res)
+        fatfs_list_directory_start(&_fs, dir, cluster);
+
+    FL_UNLOCK(&_fs);
+
+    return cluster != FAT32_INVALID_CLUSTER ? dir : 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_readdir: Get next item in directory
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_readdir(FL_DIR *dirls, fl_dirent *entry)
+{
+    int res = 0;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+
+    res = fatfs_list_directory_next(&_fs, dirls, entry);
+
+    FL_UNLOCK(&_fs);
+
+    return res ? 0 : -1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_closedir: Close directory after listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_closedir(FL_DIR* dir)
+{
+    // Not used
+    return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_is_dir: Is this a directory?
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_is_dir(const char *path)
+{
+    int res = 0;
+    FL_DIR dir;
+
+    if (fl_opendir(path, &dir))
+    {
+        res = 1;
+        fl_closedir(&dir);
+    }
+
+    return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_format: Format a partition with either FAT16 or FAT32 based on size
+//-----------------------------------------------------------------------------
+#if FATFS_INC_FORMAT_SUPPORT
+int fl_format(uint32 volume_sectors, const char *name)
+{
+    return fatfs_format(&_fs, volume_sectors, name);
+}
+#endif /*FATFS_INC_FORMAT_SUPPORT*/
+//-----------------------------------------------------------------------------
+// fl_get_fs:
+//-----------------------------------------------------------------------------
+#ifdef FATFS_INC_TEST_HOOKS
+struct fatfs* fl_get_fs(void)
+{
+    return &_fs;
+}
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_filelib.h b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_filelib.h
new file mode 100644 (file)
index 0000000..83cc877
--- /dev/null
@@ -0,0 +1,148 @@
+#ifndef __FAT_FILELIB_H__
+#define __FAT_FILELIB_H__
+
+#include "fat_opts.h"
+#include "fat_access.h"
+#include "fat_list.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#ifndef SEEK_CUR
+    #define SEEK_CUR    1
+#endif
+
+#ifndef SEEK_END
+    #define SEEK_END    2
+#endif
+
+#ifndef SEEK_SET
+    #define SEEK_SET    0
+#endif
+
+#ifndef EOF
+    #define EOF         (-1)
+#endif
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct sFL_FILE;
+
+struct cluster_lookup
+{
+    uint32 ClusterIdx;
+    uint32 CurrentCluster;
+};
+
+typedef struct sFL_FILE
+{
+    uint32                  parentcluster;
+    uint32                  startcluster;
+    uint32                  bytenum;
+    uint32                  filelength;
+    int                     filelength_changed;
+    char                    path[FATFS_MAX_LONG_FILENAME];
+    char                    filename[FATFS_MAX_LONG_FILENAME];
+    uint8                   shortfilename[11];
+
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+    uint32                  cluster_cache_idx[FAT_CLUSTER_CACHE_ENTRIES];
+    uint32                  cluster_cache_data[FAT_CLUSTER_CACHE_ENTRIES];
+#endif
+
+    // Cluster Lookup
+    struct cluster_lookup   last_fat_lookup;
+
+    // Read/Write sector buffer
+    uint8                   file_data_sector[FAT_SECTOR_SIZE];
+    uint32                  file_data_address;
+    int                     file_data_dirty;
+
+    // File fopen flags
+    uint8                   flags;
+#define FILE_READ           (1 << 0)
+#define FILE_WRITE          (1 << 1)
+#define FILE_APPEND         (1 << 2)
+#define FILE_BINARY         (1 << 3)
+#define FILE_ERASE          (1 << 4)
+#ifndef FILE_CREATE
+#define FILE_CREATE         (1 << 5)
+#endif
+
+    struct fat_node         list_node;
+} FL_FILE;
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+
+// External
+void                fl_init(void);
+void                fl_attach_locks(void (*lock)(void), void (*unlock)(void));
+int                 fl_attach_media(fn_diskio_read rd, fn_diskio_write wr);
+void                fl_shutdown(void);
+
+// Standard API
+void*               fl_fopen(const char *path, const char *modifiers);
+void                fl_fclose(void *file);
+int                 fl_fflush(void *file);
+int                 fl_fgetc(void *file);
+char *              fl_fgets(char *s, int n, void *f);
+int                 fl_fputc(int c, void *file);
+int                 fl_fputs(const char * str, void *file);
+int                 fl_fwrite(const void * data, int size, int count, void *file );
+int                 fl_fread(void * data, int size, int count, void *file );
+int                 fl_fseek(void *file , long offset , int origin );
+int                 fl_fgetpos(void *file , uint32 * position);
+long                fl_ftell(void *f);
+int                 fl_feof(void *f);
+int                 fl_remove(const char * filename);
+
+// Equivelant dirent.h
+typedef struct fs_dir_list_status    FL_DIR;
+typedef struct fs_dir_ent            fl_dirent;
+
+FL_DIR*             fl_opendir(const char* path, FL_DIR *dir);
+int                 fl_readdir(FL_DIR *dirls, fl_dirent *entry);
+int                 fl_closedir(FL_DIR* dir);
+
+// Extensions
+void                fl_listdirectory(const char *path);
+int                 fl_createdirectory(const char *path);
+int                 fl_is_dir(const char *path);
+
+int                 fl_format(uint32 volume_sectors, const char *name);
+
+// Test hooks
+#ifdef FATFS_INC_TEST_HOOKS
+struct fatfs*       fl_get_fs(void);
+#endif
+
+//-----------------------------------------------------------------------------
+// Stdio file I/O names
+//-----------------------------------------------------------------------------
+#ifdef USE_FILELIB_STDIO_COMPAT_NAMES
+
+#define FILE            FL_FILE
+
+#define fopen(a,b)      fl_fopen(a, b)
+#define fclose(a)       fl_fclose(a)
+#define fflush(a)       fl_fflush(a)
+#define fgetc(a)        fl_fgetc(a)
+#define fgets(a,b,c)    fl_fgets(a, b, c)
+#define fputc(a,b)      fl_fputc(a, b)
+#define fputs(a,b)      fl_fputs(a, b)
+#define fwrite(a,b,c,d) fl_fwrite(a, b, c, d)
+#define fread(a,b,c,d)  fl_fread(a, b, c, d)
+#define fseek(a,b,c)    fl_fseek(a, b, c)
+#define fgetpos(a,b)    fl_fgetpos(a, b)
+#define ftell(a)        fl_ftell(a)
+#define feof(a)         fl_feof(a)
+#define remove(a)       fl_remove(a)
+#define mkdir(a)        fl_createdirectory(a)
+#define rmdir(a)        0
+
+#endif
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_format.c b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_format.c
new file mode 100644 (file)
index 0000000..fee8056
--- /dev/null
@@ -0,0 +1,532 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//                            FAT16/32 File IO Library
+//                                    V2.6
+//                              Ultra-Embedded.com
+//                            Copyright 2003 - 2012
+//
+//                         Email: admin@ultra-embedded.com
+//
+//                                License: GPL
+//   If you would like a version with a more permissive license for use in
+//   closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+#include "fat_format.h"
+
+#if FATFS_INC_FORMAT_SUPPORT
+
+//-----------------------------------------------------------------------------
+// Tables
+//-----------------------------------------------------------------------------
+struct sec_per_clus_table
+{
+    uint32  sectors;
+    uint8   sectors_per_cluster;
+};
+
+struct sec_per_clus_table _cluster_size_table16[] =
+{
+    { 32680, 2},    // 16MB - 1K
+    { 262144, 4},   // 128MB - 2K
+    { 524288, 8},   // 256MB - 4K
+    { 1048576, 16}, // 512MB - 8K
+    { 2097152, 32}, // 1GB - 16K
+    { 4194304, 64}, // 2GB - 32K
+    { 8388608, 128},// 2GB - 64K [Warning only supported by Windows XP onwards]
+    { 0 , 0 }       // Invalid
+};
+
+struct sec_per_clus_table _cluster_size_table32[] =
+{
+    { 532480, 1},     // 260MB - 512b
+    { 16777216, 8},   // 8GB - 4K
+    { 33554432, 16},  // 16GB - 8K
+    { 67108864, 32},  // 32GB - 16K
+    { 0xFFFFFFFF, 64},// >32GB - 32K
+    { 0 , 0 }         // Invalid
+};
+
+//-----------------------------------------------------------------------------
+// fatfs_calc_cluster_size: Calculate what cluster size should be used
+//-----------------------------------------------------------------------------
+static uint8 fatfs_calc_cluster_size(uint32 sectors, int is_fat32)
+{
+    int i;
+
+    if (!is_fat32)
+    {
+        for (i=0; _cluster_size_table16[i].sectors_per_cluster != 0;i++)
+            if (sectors <= _cluster_size_table16[i].sectors)
+                return _cluster_size_table16[i].sectors_per_cluster;
+    }
+    else
+    {
+        for (i=0; _cluster_size_table32[i].sectors_per_cluster != 0;i++)
+            if (sectors <= _cluster_size_table32[i].sectors)
+                return _cluster_size_table32[i].sectors_per_cluster;
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_erase_sectors: Erase a number of sectors
+//-----------------------------------------------------------------------------
+static int fatfs_erase_sectors(struct fatfs *fs, uint32 lba, int count)
+{
+    int i;
+
+    // Zero sector first
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+    for (i=0;i<count;i++)
+        if (!fs->disk_io.write_media(lba + i, fs->currentsector.sector, 1))
+            return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_boot_sector: Create the boot sector
+//-----------------------------------------------------------------------------
+static int fatfs_create_boot_sector(struct fatfs *fs, uint32 boot_sector_lba, uint32 vol_sectors, const char *name, int is_fat32)
+{
+    uint32 total_clusters;
+    int i;
+
+    // Zero sector initially
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+    // OEM Name & Jump Code
+    fs->currentsector.sector[0] = 0xEB;
+    fs->currentsector.sector[1] = 0x3C;
+    fs->currentsector.sector[2] = 0x90;
+    fs->currentsector.sector[3] = 0x4D;
+    fs->currentsector.sector[4] = 0x53;
+    fs->currentsector.sector[5] = 0x44;
+    fs->currentsector.sector[6] = 0x4F;
+    fs->currentsector.sector[7] = 0x53;
+    fs->currentsector.sector[8] = 0x35;
+    fs->currentsector.sector[9] = 0x2E;
+    fs->currentsector.sector[10] = 0x30;
+
+    // Bytes per sector
+    fs->currentsector.sector[11] = (FAT_SECTOR_SIZE >> 0) & 0xFF;
+    fs->currentsector.sector[12] = (FAT_SECTOR_SIZE >> 8) & 0xFF;
+
+    // Get sectors per cluster size for the disk
+    fs->sectors_per_cluster = fatfs_calc_cluster_size(vol_sectors, is_fat32);
+    if (!fs->sectors_per_cluster)
+        return 0; // Invalid disk size
+
+    // Sectors per cluster
+    fs->currentsector.sector[13] = fs->sectors_per_cluster;
+
+    // Reserved Sectors
+    if (!is_fat32)
+        fs->reserved_sectors = 8;
+    else
+        fs->reserved_sectors = 32;
+    fs->currentsector.sector[14] = (fs->reserved_sectors >> 0) & 0xFF;
+    fs->currentsector.sector[15] = (fs->reserved_sectors >> 8) & 0xFF;
+
+    // Number of FATS
+    fs->num_of_fats = 2;
+    fs->currentsector.sector[16] = fs->num_of_fats;
+
+    // Max entries in root dir (FAT16 only)
+    if (!is_fat32)
+    {
+        fs->root_entry_count = 512;
+        fs->currentsector.sector[17] = (fs->root_entry_count >> 0) & 0xFF;
+        fs->currentsector.sector[18] = (fs->root_entry_count >> 8) & 0xFF;
+    }
+    else
+    {
+        fs->root_entry_count = 0;
+        fs->currentsector.sector[17] = 0;
+        fs->currentsector.sector[18] = 0;
+    }
+
+    // [FAT16] Total sectors (use FAT32 count instead)
+    fs->currentsector.sector[19] = 0x00;
+    fs->currentsector.sector[20] = 0x00;
+
+    // Media type
+    fs->currentsector.sector[21] = 0xF8;
+
+
+    // FAT16 BS Details
+    if (!is_fat32)
+    {
+        // Count of sectors used by the FAT table (FAT16 only)
+        total_clusters = (vol_sectors / fs->sectors_per_cluster) + 1;
+        fs->fat_sectors = (total_clusters/(FAT_SECTOR_SIZE/2)) + 1;
+        fs->currentsector.sector[22] = (uint8)((fs->fat_sectors >> 0) & 0xFF);
+        fs->currentsector.sector[23] = (uint8)((fs->fat_sectors >> 8) & 0xFF);
+
+        // Sectors per track
+        fs->currentsector.sector[24] = 0x00;
+        fs->currentsector.sector[25] = 0x00;
+
+        // Heads
+        fs->currentsector.sector[26] = 0x00;
+        fs->currentsector.sector[27] = 0x00;
+
+        // Hidden sectors
+        fs->currentsector.sector[28] = 0x20;
+        fs->currentsector.sector[29] = 0x00;
+        fs->currentsector.sector[30] = 0x00;
+        fs->currentsector.sector[31] = 0x00;
+
+        // Total sectors for this volume
+        fs->currentsector.sector[32] = (uint8)((vol_sectors>>0)&0xFF);
+        fs->currentsector.sector[33] = (uint8)((vol_sectors>>8)&0xFF);
+        fs->currentsector.sector[34] = (uint8)((vol_sectors>>16)&0xFF);
+        fs->currentsector.sector[35] = (uint8)((vol_sectors>>24)&0xFF);
+
+        // Drive number
+        fs->currentsector.sector[36] = 0x00;
+
+        // Reserved
+        fs->currentsector.sector[37] = 0x00;
+
+        // Boot signature
+        fs->currentsector.sector[38] = 0x29;
+
+        // Volume ID
+        fs->currentsector.sector[39] = 0x12;
+        fs->currentsector.sector[40] = 0x34;
+        fs->currentsector.sector[41] = 0x56;
+        fs->currentsector.sector[42] = 0x78;
+
+        // Volume name
+        for (i=0;i<11;i++)
+        {
+            if (i < (int)strlen(name))
+                fs->currentsector.sector[i+43] = name[i];
+            else
+                fs->currentsector.sector[i+43] = ' ';
+        }
+
+        // File sys type
+        fs->currentsector.sector[54] = 'F';
+        fs->currentsector.sector[55] = 'A';
+        fs->currentsector.sector[56] = 'T';
+        fs->currentsector.sector[57] = '1';
+        fs->currentsector.sector[58] = '6';
+        fs->currentsector.sector[59] = ' ';
+        fs->currentsector.sector[60] = ' ';
+        fs->currentsector.sector[61] = ' ';
+
+        // Signature
+        fs->currentsector.sector[510] = 0x55;
+        fs->currentsector.sector[511] = 0xAA;
+    }
+    // FAT32 BS Details
+    else
+    {
+        // Count of sectors used by the FAT table (FAT16 only)
+        fs->currentsector.sector[22] = 0;
+        fs->currentsector.sector[23] = 0;
+
+        // Sectors per track (default)
+        fs->currentsector.sector[24] = 0x3F;
+        fs->currentsector.sector[25] = 0x00;
+
+        // Heads (default)
+        fs->currentsector.sector[26] = 0xFF;
+        fs->currentsector.sector[27] = 0x00;
+
+        // Hidden sectors
+        fs->currentsector.sector[28] = 0x00;
+        fs->currentsector.sector[29] = 0x00;
+        fs->currentsector.sector[30] = 0x00;
+        fs->currentsector.sector[31] = 0x00;
+
+        // Total sectors for this volume
+        fs->currentsector.sector[32] = (uint8)((vol_sectors>>0)&0xFF);
+        fs->currentsector.sector[33] = (uint8)((vol_sectors>>8)&0xFF);
+        fs->currentsector.sector[34] = (uint8)((vol_sectors>>16)&0xFF);
+        fs->currentsector.sector[35] = (uint8)((vol_sectors>>24)&0xFF);
+
+        total_clusters = (vol_sectors / fs->sectors_per_cluster) + 1;
+        fs->fat_sectors = (total_clusters/(FAT_SECTOR_SIZE/4)) + 1;
+
+        // BPB_FATSz32
+        fs->currentsector.sector[36] = (uint8)((fs->fat_sectors>>0)&0xFF);
+        fs->currentsector.sector[37] = (uint8)((fs->fat_sectors>>8)&0xFF);
+        fs->currentsector.sector[38] = (uint8)((fs->fat_sectors>>16)&0xFF);
+        fs->currentsector.sector[39] = (uint8)((fs->fat_sectors>>24)&0xFF);
+
+        // BPB_ExtFlags
+        fs->currentsector.sector[40] = 0;
+        fs->currentsector.sector[41] = 0;
+
+        // BPB_FSVer
+        fs->currentsector.sector[42] = 0;
+        fs->currentsector.sector[43] = 0;
+
+        // BPB_RootClus
+        fs->currentsector.sector[44] = (uint8)((fs->rootdir_first_cluster>>0)&0xFF);
+        fs->currentsector.sector[45] = (uint8)((fs->rootdir_first_cluster>>8)&0xFF);
+        fs->currentsector.sector[46] = (uint8)((fs->rootdir_first_cluster>>16)&0xFF);
+        fs->currentsector.sector[47] = (uint8)((fs->rootdir_first_cluster>>24)&0xFF);
+
+        // BPB_FSInfo
+        fs->currentsector.sector[48] = (uint8)((fs->fs_info_sector>>0)&0xFF);
+        fs->currentsector.sector[49] = (uint8)((fs->fs_info_sector>>8)&0xFF);
+
+        // BPB_BkBootSec
+        fs->currentsector.sector[50] = 6;
+        fs->currentsector.sector[51] = 0;
+
+        // Drive number
+        fs->currentsector.sector[64] = 0x00;
+
+        // Boot signature
+        fs->currentsector.sector[66] = 0x29;
+
+        // Volume ID
+        fs->currentsector.sector[67] = 0x12;
+        fs->currentsector.sector[68] = 0x34;
+        fs->currentsector.sector[69] = 0x56;
+        fs->currentsector.sector[70] = 0x78;
+
+        // Volume name
+        for (i=0;i<11;i++)
+        {
+            if (i < (int)strlen(name))
+                fs->currentsector.sector[i+71] = name[i];
+            else
+                fs->currentsector.sector[i+71] = ' ';
+        }
+
+        // File sys type
+        fs->currentsector.sector[82] = 'F';
+        fs->currentsector.sector[83] = 'A';
+        fs->currentsector.sector[84] = 'T';
+        fs->currentsector.sector[85] = '3';
+        fs->currentsector.sector[86] = '2';
+        fs->currentsector.sector[87] = ' ';
+        fs->currentsector.sector[88] = ' ';
+        fs->currentsector.sector[89] = ' ';
+
+        // Signature
+        fs->currentsector.sector[510] = 0x55;
+        fs->currentsector.sector[511] = 0xAA;
+    }
+
+    if (fs->disk_io.write_media(boot_sector_lba, fs->currentsector.sector, 1))
+        return 1;
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_fsinfo_sector: Create the FSInfo sector (FAT32)
+//-----------------------------------------------------------------------------
+static int fatfs_create_fsinfo_sector(struct fatfs *fs, uint32 sector_lba)
+{
+    // Zero sector initially
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+    // FSI_LeadSig
+    fs->currentsector.sector[0] = 0x52;
+    fs->currentsector.sector[1] = 0x52;
+    fs->currentsector.sector[2] = 0x61;
+    fs->currentsector.sector[3] = 0x41;
+
+    // FSI_StrucSig
+    fs->currentsector.sector[484] = 0x72;
+    fs->currentsector.sector[485] = 0x72;
+    fs->currentsector.sector[486] = 0x41;
+    fs->currentsector.sector[487] = 0x61;
+
+    // FSI_Free_Count
+    fs->currentsector.sector[488] = 0xFF;
+    fs->currentsector.sector[489] = 0xFF;
+    fs->currentsector.sector[490] = 0xFF;
+    fs->currentsector.sector[491] = 0xFF;
+
+    // FSI_Nxt_Free
+    fs->currentsector.sector[492] = 0xFF;
+    fs->currentsector.sector[493] = 0xFF;
+    fs->currentsector.sector[494] = 0xFF;
+    fs->currentsector.sector[495] = 0xFF;
+
+    // Signature
+    fs->currentsector.sector[510] = 0x55;
+    fs->currentsector.sector[511] = 0xAA;
+
+    if (fs->disk_io.write_media(sector_lba, fs->currentsector.sector, 1))
+        return 1;
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_erase_fat: Erase FAT table using fs details in fs struct
+//-----------------------------------------------------------------------------
+static int fatfs_erase_fat(struct fatfs *fs, int is_fat32)
+{
+    uint32 i;
+
+    // Zero sector initially
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+    // Initialise default allocate / reserved clusters
+    if (!is_fat32)
+    {
+        SET_16BIT_WORD(fs->currentsector.sector, 0, 0xFFF8);
+        SET_16BIT_WORD(fs->currentsector.sector, 2, 0xFFFF);
+    }
+    else
+    {
+        SET_32BIT_WORD(fs->currentsector.sector, 0, 0x0FFFFFF8);
+        SET_32BIT_WORD(fs->currentsector.sector, 4, 0xFFFFFFFF);
+        SET_32BIT_WORD(fs->currentsector.sector, 8, 0x0FFFFFFF);
+    }
+
+    if (!fs->disk_io.write_media(fs->fat_begin_lba + 0, fs->currentsector.sector, 1))
+        return 0;
+
+    // Zero remaining FAT sectors
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+    for (i=1;i<fs->fat_sectors*fs->num_of_fats;i++)
+        if (!fs->disk_io.write_media(fs->fat_begin_lba + i, fs->currentsector.sector, 1))
+            return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format_fat16: Format a FAT16 partition
+//-----------------------------------------------------------------------------
+int fatfs_format_fat16(struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+    fs->currentsector.address = FAT32_INVALID_CLUSTER;
+    fs->currentsector.dirty = 0;
+
+    fs->next_free_cluster = 0; // Invalid
+
+    fatfs_fat_init(fs);
+
+    // Make sure we have read + write functions
+    if (!fs->disk_io.read_media || !fs->disk_io.write_media)
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // Volume is FAT16
+    fs->fat_type = FAT_TYPE_16;
+
+    // Not valid for FAT16
+    fs->fs_info_sector = 0;
+    fs->rootdir_first_cluster = 0;
+
+    // Sector 0: Boot sector
+    // NOTE: We don't need an MBR, it is a waste of a good sector!
+    fs->lba_begin = 0;
+    if (!fatfs_create_boot_sector(fs, fs->lba_begin, volume_sectors, name, 0))
+        return 0;
+
+    // For FAT16 (which this may be), rootdir_first_cluster is actuall rootdir_first_sector
+    fs->rootdir_first_sector = fs->reserved_sectors + (fs->num_of_fats * fs->fat_sectors);
+    fs->rootdir_sectors = ((fs->root_entry_count * 32) + (FAT_SECTOR_SIZE - 1)) / FAT_SECTOR_SIZE;
+
+    // First FAT LBA address
+    fs->fat_begin_lba = fs->lba_begin + fs->reserved_sectors;
+
+    // The address of the first data cluster on this volume
+    fs->cluster_begin_lba = fs->fat_begin_lba + (fs->num_of_fats * fs->fat_sectors);
+
+    // Initialise FAT sectors
+    if (!fatfs_erase_fat(fs, 0))
+        return 0;
+
+    // Erase Root directory
+    if (!fatfs_erase_sectors(fs, fs->lba_begin + fs->rootdir_first_sector, fs->rootdir_sectors))
+        return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format_fat32: Format a FAT32 partition
+//-----------------------------------------------------------------------------
+int fatfs_format_fat32(struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+    fs->currentsector.address = FAT32_INVALID_CLUSTER;
+    fs->currentsector.dirty = 0;
+
+    fs->next_free_cluster = 0; // Invalid
+
+    fatfs_fat_init(fs);
+
+    // Make sure we have read + write functions
+    if (!fs->disk_io.read_media || !fs->disk_io.write_media)
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // Volume is FAT32
+    fs->fat_type = FAT_TYPE_32;
+
+    // Basic defaults for normal FAT32 partitions
+    fs->fs_info_sector = 1;
+    fs->rootdir_first_cluster = 2;
+
+    // Sector 0: Boot sector
+    // NOTE: We don't need an MBR, it is a waste of a good sector!
+    fs->lba_begin = 0;
+    if (!fatfs_create_boot_sector(fs, fs->lba_begin, volume_sectors, name, 1))
+        return 0;
+
+    // First FAT LBA address
+    fs->fat_begin_lba = fs->lba_begin + fs->reserved_sectors;
+
+    // The address of the first data cluster on this volume
+    fs->cluster_begin_lba = fs->fat_begin_lba + (fs->num_of_fats * fs->fat_sectors);
+
+    // Initialise FSInfo sector
+    if (!fatfs_create_fsinfo_sector(fs, fs->fs_info_sector))
+        return 0;
+
+    // Initialise FAT sectors
+    if (!fatfs_erase_fat(fs, 1))
+        return 0;
+
+    // Erase Root directory
+    if (!fatfs_erase_sectors(fs, fatfs_lba_of_cluster(fs, fs->rootdir_first_cluster), fs->sectors_per_cluster))
+        return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format: Format a partition with either FAT16 or FAT32 based on size
+//-----------------------------------------------------------------------------
+int fatfs_format(struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+    // 2GB - 32K limit for safe behaviour for FAT16
+    if (volume_sectors <= 4194304)
+        return fatfs_format_fat16(fs, volume_sectors, name);
+    else
+        return fatfs_format_fat32(fs, volume_sectors, name);
+}
+#endif /*FATFS_INC_FORMAT_SUPPORT*/
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_format.h b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_format.h
new file mode 100644 (file)
index 0000000..a8a6bba
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __FAT_FORMAT_H__
+#define __FAT_FORMAT_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+#include "fat_access.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_format(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat16(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat32(struct fatfs *fs, uint32 volume_sectors, const char *name);
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_list.h b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_list.h
new file mode 100644 (file)
index 0000000..bd386ef
--- /dev/null
@@ -0,0 +1,161 @@
+#ifndef __FAT_LIST_H__
+#define __FAT_LIST_H__
+
+#ifndef FAT_ASSERT
+    #define FAT_ASSERT(x)
+#endif
+
+#ifndef FAT_INLINE
+    #define FAT_INLINE
+#endif
+
+//-----------------------------------------------------------------
+// Types
+//-----------------------------------------------------------------
+struct fat_list;
+
+struct fat_node
+{
+    struct fat_node    *previous;
+    struct fat_node    *next;
+};
+
+struct fat_list
+{
+    struct fat_node    *head;
+    struct fat_node    *tail;
+};
+
+//-----------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------
+#define fat_list_entry(p, t, m)     p ? ((t *)((char *)(p)-(char*)(&((t *)0)->m))) : 0
+#define fat_list_next(l, p)         (p)->next
+#define fat_list_prev(l, p)         (p)->previous
+#define fat_list_first(l)           (l)->head
+#define fat_list_last(l)            (l)->tail
+#define fat_list_for_each(l, p)     for ((p) = (l)->head; (p); (p) = (p)->next)
+
+//-----------------------------------------------------------------
+// Inline Functions
+//-----------------------------------------------------------------
+
+//-----------------------------------------------------------------
+// fat_list_init:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_init(struct fat_list *list)
+{
+    FAT_ASSERT(list);
+
+    list->head = list->tail = 0;
+}
+//-----------------------------------------------------------------
+// fat_list_remove:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_remove(struct fat_list *list, struct fat_node *node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+
+    if(!node->previous)
+        list->head = node->next;
+    else
+        node->previous->next = node->next;
+
+    if(!node->next)
+        list->tail = node->previous;
+    else
+        node->next->previous = node->previous;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_after:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_after(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+    FAT_ASSERT(new_node);
+
+    new_node->previous = node;
+    new_node->next = node->next;
+    if (!node->next)
+        list->tail = new_node;
+    else
+        node->next->previous = new_node;
+    node->next = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_before:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_before(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+    FAT_ASSERT(new_node);
+
+    new_node->previous = node->previous;
+    new_node->next = node;
+    if (!node->previous)
+        list->head = new_node;
+    else
+        node->previous->next = new_node;
+    node->previous = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_first:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_first(struct fat_list *list, struct fat_node *node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+
+    if (!list->head)
+    {
+        list->head = node;
+        list->tail = node;
+        node->previous = 0;
+        node->next = 0;
+    }
+    else
+        fat_list_insert_before(list, list->head, node);
+}
+//-----------------------------------------------------------------
+// fat_list_insert_last:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_last(struct fat_list *list, struct fat_node *node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+
+    if (!list->tail)
+        fat_list_insert_first(list, node);
+     else
+        fat_list_insert_after(list, list->tail, node);
+}
+//-----------------------------------------------------------------
+// fat_list_is_empty:
+//-----------------------------------------------------------------
+static FAT_INLINE int fat_list_is_empty(struct fat_list *list)
+{
+    FAT_ASSERT(list);
+
+    return !list->head;
+}
+//-----------------------------------------------------------------
+// fat_list_pop_head:
+//-----------------------------------------------------------------
+static FAT_INLINE struct fat_node * fat_list_pop_head(struct fat_list *list)
+{
+    struct fat_node * node;
+
+    FAT_ASSERT(list);
+
+    node = fat_list_first(list);
+    if (node)
+        fat_list_remove(list, node);
+
+    return node;
+}
+
+#endif
+
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_misc.c b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_misc.c
new file mode 100644 (file)
index 0000000..4c7dfab
--- /dev/null
@@ -0,0 +1,505 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//                            FAT16/32 File IO Library
+//                                    V2.6
+//                              Ultra-Embedded.com
+//                            Copyright 2003 - 2012
+//
+//                         Email: admin@ultra-embedded.com
+//
+//                                License: GPL
+//   If you would like a version with a more permissive license for use in
+//   closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include <stdlib.h>
+#include <string.h>
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_init: Clear long file name cache
+//-----------------------------------------------------------------------------
+void fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable)
+{
+    int i = 0;
+
+    lfn->no_of_strings = 0;
+
+#if FATFS_INC_LFN_SUPPORT
+
+    // Zero out buffer also
+    if (wipeTable)
+        for (i=0;i<MAX_LONGFILENAME_ENTRIES;i++)
+            memset(lfn->String[i], 0x00, MAX_LFN_ENTRY_LENGTH);
+#endif
+}
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_entry - Function extracts long file name text from sector
+// at a specific offset
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+void fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer)
+{
+    uint8 LFNIndex, i;
+    LFNIndex = entryBuffer[0] & 0x1F;
+
+    // Limit file name to cache size!
+    if (LFNIndex > MAX_LONGFILENAME_ENTRIES)
+        return ;
+
+    // This is an error condition
+    if (LFNIndex == 0)
+        return ;
+
+    if (lfn->no_of_strings == 0)
+        lfn->no_of_strings = LFNIndex;
+
+    lfn->String[LFNIndex-1][0] = entryBuffer[1];
+    lfn->String[LFNIndex-1][1] = entryBuffer[3];
+    lfn->String[LFNIndex-1][2] = entryBuffer[5];
+    lfn->String[LFNIndex-1][3] = entryBuffer[7];
+    lfn->String[LFNIndex-1][4] = entryBuffer[9];
+    lfn->String[LFNIndex-1][5] = entryBuffer[0x0E];
+    lfn->String[LFNIndex-1][6] = entryBuffer[0x10];
+    lfn->String[LFNIndex-1][7] = entryBuffer[0x12];
+    lfn->String[LFNIndex-1][8] = entryBuffer[0x14];
+    lfn->String[LFNIndex-1][9] = entryBuffer[0x16];
+    lfn->String[LFNIndex-1][10] = entryBuffer[0x18];
+    lfn->String[LFNIndex-1][11] = entryBuffer[0x1C];
+    lfn->String[LFNIndex-1][12] = entryBuffer[0x1E];
+
+    for (i=0; i<MAX_LFN_ENTRY_LENGTH; i++)
+        if (lfn->String[LFNIndex-1][i]==0xFF)
+            lfn->String[LFNIndex-1][i] = 0x20; // Replace with spaces
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_get: Get a reference to the long filename
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+char* fatfs_lfn_cache_get(struct lfn_cache *lfn)
+{
+    // Null terminate long filename
+    if (lfn->no_of_strings == MAX_LONGFILENAME_ENTRIES)
+        lfn->Null = '\0';
+    else if (lfn->no_of_strings)
+        lfn->String[lfn->no_of_strings][0] = '\0';
+    else
+        lfn->String[0][0] = '\0';
+
+    return (char*)&lfn->String[0][0];
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_text: If LFN text entry found
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_text(struct fat_dir_entry *entry)
+{
+    if ((entry->Attr & FILE_ATTR_LFN_TEXT) == FILE_ATTR_LFN_TEXT)
+        return 1;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_invalid: If SFN found not relating to LFN
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_invalid(struct fat_dir_entry *entry)
+{
+    if ( (entry->Name[0]==FILE_HEADER_BLANK)  ||
+         (entry->Name[0]==FILE_HEADER_DELETED)||
+         (entry->Attr==FILE_ATTR_VOLUME_ID) ||
+         (entry->Attr & FILE_ATTR_SYSHID) )
+        return 1;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_exists: If LFN exists and correlation SFN found
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry)
+{
+    if ( (entry->Attr!=FILE_ATTR_LFN_TEXT) &&
+         (entry->Name[0]!=FILE_HEADER_BLANK) &&
+         (entry->Name[0]!=FILE_HEADER_DELETED) &&
+         (entry->Attr!=FILE_ATTR_VOLUME_ID) &&
+         (!(entry->Attr&FILE_ATTR_SYSHID)) &&
+         (lfn->no_of_strings) )
+        return 1;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_sfn_only: If SFN only exists
+//-----------------------------------------------------------------------------
+int fatfs_entry_sfn_only(struct fat_dir_entry *entry)
+{
+    if ( (entry->Attr!=FILE_ATTR_LFN_TEXT) &&
+         (entry->Name[0]!=FILE_HEADER_BLANK) &&
+         (entry->Name[0]!=FILE_HEADER_DELETED) &&
+         (entry->Attr!=FILE_ATTR_VOLUME_ID) &&
+         (!(entry->Attr&FILE_ATTR_SYSHID)) )
+        return 1;
+    else
+        return 0;
+}
+// TODO: FILE_ATTR_SYSHID ?!?!??!
+//-----------------------------------------------------------------------------
+// fatfs_entry_is_dir: Returns 1 if a directory
+//-----------------------------------------------------------------------------
+int fatfs_entry_is_dir(struct fat_dir_entry *entry)
+{
+    if (entry->Attr & FILE_TYPE_DIR)
+        return 1;
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_entry_is_file: Returns 1 is a file entry
+//-----------------------------------------------------------------------------
+int fatfs_entry_is_file(struct fat_dir_entry *entry)
+{
+    if (entry->Attr & FILE_TYPE_FILE)
+        return 1;
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_lfn_entries_required: Calculate number of 13 characters entries
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_lfn_entries_required(char *filename)
+{
+    int length = (int)strlen(filename);
+
+    if (length)
+        return (length + MAX_LFN_ENTRY_LENGTH - 1) / MAX_LFN_ENTRY_LENGTH;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_filename_to_lfn:
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+void fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk)
+{
+    int i;
+    int nameIndexes[MAX_LFN_ENTRY_LENGTH] = {1,3,5,7,9,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E};
+
+    // 13 characters entries
+    int length = (int)strlen(filename);
+    int entriesRequired = fatfs_lfn_entries_required(filename);
+
+    // Filename offset
+    int start = entry * MAX_LFN_ENTRY_LENGTH;
+
+    // Initialise to zeros
+    memset(buffer, 0x00, FAT_DIR_ENTRY_SIZE);
+
+    // LFN entry number
+    buffer[0] = (uint8)(((entriesRequired-1)==entry)?(0x40|(entry+1)):(entry+1));
+
+    // LFN flag
+    buffer[11] = 0x0F;
+
+    // Checksum of short filename
+    buffer[13] = sfnChk;
+
+    // Copy to buffer
+    for (i=0;i<MAX_LFN_ENTRY_LENGTH;i++)
+    {
+        if ( (start+i) < length )
+            buffer[nameIndexes[i]] = filename[start+i];
+        else if ( (start+i) == length )
+            buffer[nameIndexes[i]] = 0x00;
+        else
+        {
+            buffer[nameIndexes[i]] = 0xFF;
+            buffer[nameIndexes[i]+1] = 0xFF;
+        }
+    }
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_sfn_create_entry: Create the short filename directory entry
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+void fatfs_sfn_create_entry(char *shortfilename, uint32 size, uint32 startCluster, struct fat_dir_entry *entry, int dir)
+{
+    int i;
+
+    // Copy short filename
+    for (i=0;i<FAT_SFN_SIZE_FULL;i++)
+        entry->Name[i] = shortfilename[i];
+
+    // Unless we have a RTC we might as well set these to 1980
+    entry->CrtTimeTenth = 0x00;
+    entry->CrtTime[1] = entry->CrtTime[0] = 0x00;
+    entry->CrtDate[1] = 0x00;
+    entry->CrtDate[0] = 0x20;
+    entry->LstAccDate[1] = 0x00;
+    entry->LstAccDate[0] = 0x20;
+    entry->WrtTime[1] = entry->WrtTime[0] = 0x00;
+    entry->WrtDate[1] = 0x00;
+    entry->WrtDate[0] = 0x20;
+
+    if (!dir)
+        entry->Attr = FILE_TYPE_FILE;
+    else
+        entry->Attr = FILE_TYPE_DIR;
+
+    entry->NTRes = 0x00;
+
+    entry->FstClusHI = FAT_HTONS((uint16)((startCluster>>16) & 0xFFFF));
+    entry->FstClusLO = FAT_HTONS((uint16)((startCluster>>0) & 0xFFFF));
+    entry->FileSize = FAT_HTONL(size);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_create_sfn: Create a padded SFN
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_lfn_create_sfn(char *sfn_output, char *filename)
+{
+    int i;
+    int dotPos = -1;
+    char ext[3];
+    int pos;
+    int len = (int)strlen(filename);
+
+    // Invalid to start with .
+    if (filename[0]=='.')
+        return 0;
+
+    memset(sfn_output, ' ', FAT_SFN_SIZE_FULL);
+    memset(ext, ' ', 3);
+
+    // Find dot seperator
+    for (i = 0; i< len; i++)
+    {
+        if (filename[i]=='.')
+            dotPos = i;
+    }
+
+    // Extract extensions
+    if (dotPos!=-1)
+    {
+        // Copy first three chars of extension
+        for (i = (dotPos+1); i < (dotPos+1+3); i++)
+            if (i<len)
+                ext[i-(dotPos+1)] = filename[i];
+
+        // Shorten the length to the dot position
+        len = dotPos;
+    }
+
+    // Add filename part
+    pos = 0;
+    for (i=0;i<len;i++)
+    {
+        if ( (filename[i]!=' ') && (filename[i]!='.') )
+        {
+            if (filename[i] >= 'a' && filename[i] <= 'z')
+                sfn_output[pos++] = filename[i] - 'a' + 'A';
+            else
+                sfn_output[pos++] = filename[i];
+        }
+
+        // Fill upto 8 characters
+        if (pos==FAT_SFN_SIZE_PARTIAL)
+            break;
+    }
+
+    // Add extension part
+    for (i=FAT_SFN_SIZE_PARTIAL;i<FAT_SFN_SIZE_FULL;i++)
+    {
+        if (ext[i-FAT_SFN_SIZE_PARTIAL] >= 'a' && ext[i-FAT_SFN_SIZE_PARTIAL] <= 'z')
+            sfn_output[i] = ext[i-FAT_SFN_SIZE_PARTIAL] - 'a' + 'A';
+        else
+            sfn_output[i] = ext[i-FAT_SFN_SIZE_PARTIAL];
+    }
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_itoa:
+//-----------------------------------------------------------------------------
+static void fatfs_itoa(uint32 num, char *s)
+{
+    char* cp;
+    char outbuf[12];
+    const char digits[] = "0123456789ABCDEF";
+
+    // Build string backwards
+    cp = outbuf;
+    do
+    {
+        *cp++ = digits[(int)(num % 10)];
+    }
+    while ((num /= 10) > 0);
+
+    *cp-- = 0;
+
+    // Copy in forwards
+    while (cp >= outbuf)
+        *s++ = *cp--;
+
+    *s = 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_generate_tail:
+// sfn_input = Input short filename, spaced format & in upper case
+// sfn_output = Output short filename with tail
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum)
+{
+    int tail_chars;
+    char tail_str[12];
+
+    if (tailNum > 99999)
+        return 0;
+
+    // Convert to number
+    memset(tail_str, 0x00, sizeof(tail_str));
+    tail_str[0] = '~';
+    fatfs_itoa(tailNum, tail_str+1);
+
+    // Copy in base filename
+    memcpy(sfn_output, sfn_input, FAT_SFN_SIZE_FULL);
+
+    // Overwrite with tail
+    tail_chars = (int)strlen(tail_str);
+    memcpy(sfn_output+(FAT_SFN_SIZE_PARTIAL-tail_chars), tail_str, tail_chars);
+
+    return 1;
+}
+#endif
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_convert_from_fat_time: Convert FAT time to h/m/s
+//-----------------------------------------------------------------------------
+#if FATFS_INC_TIME_DATE_SUPPORT
+void fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds)
+{
+    *hours = (fat_time >> FAT_TIME_HOURS_SHIFT) & FAT_TIME_HOURS_MASK;
+    *minutes = (fat_time >> FAT_TIME_MINUTES_SHIFT) & FAT_TIME_MINUTES_MASK;
+    *seconds = (fat_time >> FAT_TIME_SECONDS_SHIFT) & FAT_TIME_SECONDS_MASK;
+    *seconds = *seconds * FAT_TIME_SECONDS_SCALE;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_from_fat_date: Convert FAT date to d/m/y
+//-----------------------------------------------------------------------------
+void fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year)
+{
+    *day = (fat_date >> FAT_DATE_DAY_SHIFT) & FAT_DATE_DAY_MASK;
+    *month = (fat_date >> FAT_DATE_MONTH_SHIFT) & FAT_DATE_MONTH_MASK;
+    *year = (fat_date >> FAT_DATE_YEAR_SHIFT) & FAT_DATE_YEAR_MASK;
+    *year = *year + FAT_DATE_YEAR_OFFSET;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_to_fat_time: Convert h/m/s to FAT time
+//-----------------------------------------------------------------------------
+uint16 fatfs_convert_to_fat_time(int hours, int minutes, int seconds)
+{
+    uint16 fat_time = 0;
+
+    // Most FAT times are to a resolution of 2 seconds
+    seconds /= FAT_TIME_SECONDS_SCALE;
+
+    fat_time = (hours & FAT_TIME_HOURS_MASK) << FAT_TIME_HOURS_SHIFT;
+    fat_time|= (minutes & FAT_TIME_MINUTES_MASK) << FAT_TIME_MINUTES_SHIFT;
+    fat_time|= (seconds & FAT_TIME_SECONDS_MASK) << FAT_TIME_SECONDS_SHIFT;
+
+    return fat_time;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_to_fat_date: Convert d/m/y to FAT date
+//-----------------------------------------------------------------------------
+uint16 fatfs_convert_to_fat_date(int day, int month, int year)
+{
+    uint16 fat_date = 0;
+
+    // FAT dates are relative to 1980
+    if (year >= FAT_DATE_YEAR_OFFSET)
+        year -= FAT_DATE_YEAR_OFFSET;
+
+    fat_date = (day & FAT_DATE_DAY_MASK) << FAT_DATE_DAY_SHIFT;
+    fat_date|= (month & FAT_DATE_MONTH_MASK) << FAT_DATE_MONTH_SHIFT;
+    fat_date|= (year & FAT_DATE_YEAR_MASK) << FAT_DATE_YEAR_SHIFT;
+
+    return fat_date;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_print_sector:
+//-----------------------------------------------------------------------------
+#ifdef FATFS_DEBUG
+void fatfs_print_sector(uint32 sector, uint8 *data)
+{
+    int i;
+    int j;
+
+    FAT_PRINTF(("Sector %d:\n", sector));
+
+    for (i=0;i<FAT_SECTOR_SIZE;i++)
+    {
+        if (!((i) % 16))
+        {
+            FAT_PRINTF(("  %04d: ", i));
+        }
+
+        FAT_PRINTF(("%02x", data[i]));
+        if (!((i+1) % 4))
+        {
+            FAT_PRINTF((" "));
+        }
+
+        if (!((i+1) % 16))
+        {
+            FAT_PRINTF(("   "));
+            for (j=0;j<16;j++)
+            {
+                char ch = data[i-15+j];
+
+                // Is printable?
+                if (ch > 31 && ch < 127)
+                {
+                    FAT_PRINTF(("%c", ch));
+                }
+                else
+                {
+                    FAT_PRINTF(("."));
+                }
+            }
+
+            FAT_PRINTF(("\n"));
+        }
+    }
+}
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_misc.h b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_misc.h
new file mode 100644 (file)
index 0000000..0c02634
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef __FAT_MISC_H__
+#define __FAT_MISC_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define MAX_LONGFILENAME_ENTRIES    20
+#define MAX_LFN_ENTRY_LENGTH        13
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+#define GET_32BIT_WORD(buffer, location)    ( ((uint32)buffer[location+3]<<24) + ((uint32)buffer[location+2]<<16) + ((uint32)buffer[location+1]<<8) + (uint32)buffer[location+0] )
+#define GET_16BIT_WORD(buffer, location)    ( ((uint16)buffer[location+1]<<8) + (uint16)buffer[location+0] )
+
+#define SET_32BIT_WORD(buffer, location, value)    { buffer[location+0] = (uint8)((value)&0xFF); \
+                                                  buffer[location+1] = (uint8)((value>>8)&0xFF); \
+                                                  buffer[location+2] = (uint8)((value>>16)&0xFF); \
+                                                  buffer[location+3] = (uint8)((value>>24)&0xFF); }
+
+#define SET_16BIT_WORD(buffer, location, value)    { buffer[location+0] = (uint8)((value)&0xFF); \
+                                                  buffer[location+1] = (uint8)((value>>8)&0xFF); }
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct lfn_cache
+{
+#if FATFS_INC_LFN_SUPPORT
+    // Long File Name Structure (max 260 LFN length)
+    uint8 String[MAX_LONGFILENAME_ENTRIES][MAX_LFN_ENTRY_LENGTH];
+    uint8 Null;
+#endif
+    uint8 no_of_strings;
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void    fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable);
+void    fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer);
+char*   fatfs_lfn_cache_get(struct lfn_cache *lfn);
+int     fatfs_entry_lfn_text(struct fat_dir_entry *entry);
+int     fatfs_entry_lfn_invalid(struct fat_dir_entry *entry);
+int     fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry);
+int     fatfs_entry_sfn_only(struct fat_dir_entry *entry);
+int     fatfs_entry_is_dir(struct fat_dir_entry *entry);
+int     fatfs_entry_is_file(struct fat_dir_entry *entry);
+int     fatfs_lfn_entries_required(char *filename);
+void    fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk);
+void    fatfs_sfn_create_entry(char *shortfilename, uint32 size, uint32 startCluster, struct fat_dir_entry *entry, int dir);
+int     fatfs_lfn_create_sfn(char *sfn_output, char *filename);
+int     fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum);
+void    fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds);
+void    fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year);
+uint16  fatfs_convert_to_fat_time(int hours, int minutes, int seconds);
+uint16  fatfs_convert_to_fat_date(int day, int month, int year);
+void    fatfs_print_sector(uint32 sector, uint8 *data);
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_opts.h b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_opts.h
new file mode 100644 (file)
index 0000000..6c4728a
--- /dev/null
@@ -0,0 +1,91 @@
+#ifndef __FAT_OPTS_H__
+#define __FAT_OPTS_H__
+
+#ifdef FATFS_USE_CUSTOM_OPTS_FILE
+    #include "fat_custom.h"
+#endif
+
+//-------------------------------------------------------------
+// Configuration
+//-------------------------------------------------------------
+
+// Is the processor little endian (1) or big endian (0)
+#ifndef FATFS_IS_LITTLE_ENDIAN
+    #define FATFS_IS_LITTLE_ENDIAN          1
+#endif
+
+// Max filename Length
+#ifndef FATFS_MAX_LONG_FILENAME
+    #define FATFS_MAX_LONG_FILENAME         260
+#endif
+
+// Max open files (reduce to lower memory requirements)
+#ifndef FATFS_MAX_OPEN_FILES
+    #define FATFS_MAX_OPEN_FILES            2
+#endif
+
+// Number of sectors per FAT_BUFFER (min 1)
+#ifndef FAT_BUFFER_SECTORS
+    #define FAT_BUFFER_SECTORS              1
+#endif
+
+// Max FAT sectors to buffer (min 1)
+// (mem used is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE)
+#ifndef FAT_BUFFERS
+    #define FAT_BUFFERS                     1
+#endif
+
+// Size of cluster chain cache (can be undefined)
+// Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2
+// Improves access speed considerably
+//#define FAT_CLUSTER_CACHE_ENTRIES         128
+
+// Include support for writing files (1 / 0)?
+#ifndef FATFS_INC_WRITE_SUPPORT
+    #define FATFS_INC_WRITE_SUPPORT         1
+#endif
+
+// Support long filenames (1 / 0)?
+// (if not (0) only 8.3 format is supported)
+#ifndef FATFS_INC_LFN_SUPPORT
+    #define FATFS_INC_LFN_SUPPORT           1
+#endif
+
+// Support directory listing (1 / 0)?
+#ifndef FATFS_DIR_LIST_SUPPORT
+    #define FATFS_DIR_LIST_SUPPORT          1
+#endif
+
+// Support time/date (1 / 0)?
+#ifndef FATFS_INC_TIME_DATE_SUPPORT
+    #define FATFS_INC_TIME_DATE_SUPPORT     0
+#endif
+
+// Include support for formatting disks (1 / 0)?
+#ifndef FATFS_INC_FORMAT_SUPPORT
+    #define FATFS_INC_FORMAT_SUPPORT        1
+#endif
+
+// Sector size used
+#define FAT_SECTOR_SIZE                     512
+
+// Printf output (directory listing / debug)
+#ifndef FAT_PRINTF
+    // Don't include stdio, but there is a printf function available
+    #ifdef FAT_PRINTF_NOINC_STDIO
+        extern int printf(const char* ctrl1, ... );
+        #define FAT_PRINTF(a)               printf a
+    // Include stdio to use printf
+    #else
+        #include <stdio.h>
+        void Log(const char *fmt, ...);
+        #define FAT_PRINTF(a)               Log a
+    #endif
+#endif
+
+// Time/Date support requires time.h
+#if FATFS_INC_TIME_DATE_SUPPORT
+    #include <time.h>
+#endif
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_string.c b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_string.c
new file mode 100644 (file)
index 0000000..edf1db9
--- /dev/null
@@ -0,0 +1,514 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//                            FAT16/32 File IO Library
+//                                    V2.6
+//                              Ultra-Embedded.com
+//                            Copyright 2003 - 2012
+//
+//                         Email: admin@ultra-embedded.com
+//
+//                                License: GPL
+//   If you would like a version with a more permissive license for use in
+//   closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include <string.h>
+#include <assert.h>
+#include "fat_string.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_total_path_levels: Take a filename and path and count the sub levels
+// of folders. E.g. C:\folder\file.zip = 1 level
+// Acceptable input formats are:
+//        c:\folder\file.zip
+//        /dev/etc/samba.conf
+// Returns: -1 = Error, 0 or more = Ok
+//-----------------------------------------------------------------------------
+int fatfs_total_path_levels(char *path)
+{
+    int levels = 0;
+    char expectedchar;
+
+    if (!path)
+        return -1;
+
+    // Acceptable formats:
+    //  c:\folder\file.zip
+    //  /dev/etc/samba.conf
+    if (*path == '/')
+    {
+        expectedchar = '/';
+        path++;
+    }
+    else if (path[1] == ':' || path[2] == '\\')
+    {
+        expectedchar = '\\';
+        path += 3;
+    }
+    else
+        return -1;
+
+    // Count levels in path string
+    while (*path)
+    {
+        // Fast forward through actual subdir text to next slash
+        for (; *path; )
+        {
+            // If slash detected escape from for loop
+            if (*path == expectedchar) { path++; break; }
+            path++;
+        }
+
+        // Increase number of subdirs founds
+        levels++;
+    }
+
+    // Subtract the file itself
+    return levels-1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_substring: Get a substring from 'path' which contains the folder
+// (or file) at the specified level.
+// E.g. C:\folder\file.zip : Level 0 = C:\folder, Level 1 = file.zip
+// Returns: -1 = Error, 0 = Ok
+//-----------------------------------------------------------------------------
+int fatfs_get_substring(char *path, int levelreq, char *output, int max_len)
+{
+    int i;
+    int pathlen=0;
+    int levels=0;
+    int copypnt=0;
+    char expectedchar;
+
+    if (!path || max_len <= 0)
+        return -1;
+
+    // Acceptable formats:
+    //  c:\folder\file.zip
+    //  /dev/etc/samba.conf
+    if (*path == '/')
+    {
+        expectedchar = '/';
+        path++;
+    }
+    else if (path[1] == ':' || path[2] == '\\')
+    {
+        expectedchar = '\\';
+        path += 3;
+    }
+    else
+        return -1;
+
+    // Get string length of path
+    pathlen = (int)strlen (path);
+
+    // Loop through the number of times as characters in 'path'
+    for (i = 0; i<pathlen; i++)
+    {
+        // If a '\' is found then increase level
+        if (*path == expectedchar) levels++;
+
+        // If correct level and the character is not a '\' or '/' then copy text to 'output'
+        if ( (levels == levelreq) && (*path != expectedchar) && (copypnt < (max_len-1)))
+            output[copypnt++] = *path;
+
+        // Increment through path string
+        path++;
+    }
+
+    // Null Terminate
+    output[copypnt] = '\0';
+
+    // If a string was copied return 0 else return 1
+    if (output[0] != '\0')
+        return 0;    // OK
+    else
+        return -1;    // Error
+}
+//-----------------------------------------------------------------------------
+// fatfs_split_path: Full path contains the passed in string.
+// Returned is the path string and file Name string
+// E.g. C:\folder\file.zip -> path = C:\folder  filename = file.zip
+// E.g. C:\file.zip -> path = [blank]  filename = file.zip
+//-----------------------------------------------------------------------------
+int fatfs_split_path(char *full_path, char *path, int max_path, char *filename, int max_filename)
+{
+    int strindex;
+
+    // Count the levels to the filepath
+    int levels = fatfs_total_path_levels(full_path);
+    if (levels == -1)
+        return -1;
+
+    // Get filename part of string
+    if (fatfs_get_substring(full_path, levels, filename, max_filename) != 0)
+        return -1;
+
+    // If root file
+    if (levels == 0)
+        path[0] = '\0';
+    else
+    {
+        strindex = (int)strlen(full_path) - (int)strlen(filename);
+        if (strindex > max_path)
+            strindex = max_path;
+
+        memcpy(path, full_path, strindex);
+        path[strindex-1] = '\0';
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// FileString_StrCmpNoCase: Compare two strings case with case sensitivity
+//-----------------------------------------------------------------------------
+static int FileString_StrCmpNoCase(char *s1, char *s2, int n)
+{
+    int diff;
+    char a,b;
+
+    while (n--)
+    {
+        a = *s1;
+        b = *s2;
+
+        // Make lower case if uppercase
+        if ((a>='A') && (a<='Z'))
+            a+= 32;
+        if ((b>='A') && (b<='Z'))
+            b+= 32;
+
+        diff = a - b;
+
+        // If different
+        if (diff)
+            return diff;
+
+        // If run out of strings
+        if ( (*s1 == 0) || (*s2 == 0) )
+            break;
+
+        s1++;
+        s2++;
+    }
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// FileString_GetExtension: Get index to extension within filename
+// Returns -1 if not found or index otherwise
+//-----------------------------------------------------------------------------
+static int FileString_GetExtension(char *str)
+{
+    int dotPos = -1;
+    char *strSrc = str;
+
+    // Find last '.' in string (if at all)
+    while (*strSrc)
+    {
+        if (*strSrc=='.')
+            dotPos = (int)(strSrc-str);
+
+        strSrc++;
+    }
+
+    return dotPos;
+}
+//-----------------------------------------------------------------------------
+// FileString_TrimLength: Get length of string excluding trailing spaces
+// Returns -1 if not found or index otherwise
+//-----------------------------------------------------------------------------
+static int FileString_TrimLength(char *str, int strLen)
+{
+    int length = strLen;
+    char *strSrc = str+strLen-1;
+
+    // Find last non white space
+    while (strLen != 0)
+    {
+        if (*strSrc == ' ')
+            length = (int)(strSrc - str);
+        else
+            break;
+
+        strSrc--;
+        strLen--;
+    }
+
+    return length;
+}
+//-----------------------------------------------------------------------------
+// fatfs_compare_names: Compare two filenames (without copying or changing origonals)
+// Returns 1 if match, 0 if not
+//-----------------------------------------------------------------------------
+int fatfs_compare_names(char* strA, char* strB)
+{
+    char *ext1 = NULL;
+    char *ext2 = NULL;
+    int ext1Pos, ext2Pos;
+    int file1Len, file2Len;
+
+    // Get both files extension
+    ext1Pos = FileString_GetExtension(strA);
+    ext2Pos = FileString_GetExtension(strB);
+
+    // NOTE: Extension position can be different for matching
+    // filename if trailing space are present before it!
+    // Check that if one has an extension, so does the other
+    if ((ext1Pos==-1) && (ext2Pos!=-1))
+        return 0;
+    if ((ext2Pos==-1) && (ext1Pos!=-1))
+        return 0;
+
+    // If they both have extensions, compare them
+    if (ext1Pos!=-1)
+    {
+        // Set pointer to start of extension
+        ext1 = strA+ext1Pos+1;
+        ext2 = strB+ext2Pos+1;
+
+        // Verify that the file extension lengths match!
+        if (strlen(ext1) != strlen(ext2))
+            return 0;
+
+        // If they dont match
+        if (FileString_StrCmpNoCase(ext1, ext2, (int)strlen(ext1))!=0)
+            return 0;
+
+        // Filelength is upto extensions
+        file1Len = ext1Pos;
+        file2Len = ext2Pos;
+    }
+    // No extensions
+    else
+    {
+        // Filelength is actual filelength
+        file1Len = (int)strlen(strA);
+        file2Len = (int)strlen(strB);
+    }
+
+    // Find length without trailing spaces (before ext)
+    file1Len = FileString_TrimLength(strA, file1Len);
+    file2Len = FileString_TrimLength(strB, file2Len);
+
+    // Check the file lengths match
+    if (file1Len!=file2Len)
+        return 0;
+
+    // Compare main part of filenames
+    if (FileString_StrCmpNoCase(strA, strB, file1Len)!=0)
+        return 0;
+    else
+        return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_string_ends_with_slash: Does the string end with a slash (\ or /)
+//-----------------------------------------------------------------------------
+int fatfs_string_ends_with_slash(char *path)
+{
+    if (path)
+    {
+        while (*path)
+        {
+            // Last character?
+            if (!(*(path+1)))
+            {
+                if (*path == '\\' || *path == '/')
+                    return 1;
+            }
+
+            path++;
+        }
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_sfn_display_name: Get display name for SFN entry
+//-----------------------------------------------------------------------------
+int fatfs_get_sfn_display_name(char* out, char* in)
+{
+    int len = 0;
+    while (*in && len <= 11)
+    {
+        char a = *in++;
+
+        if (a == ' ')
+            continue;
+        // Make lower case if uppercase
+        else if ((a>='A') && (a<='Z'))
+            a+= 32;
+
+        *out++ = a;
+        len++;
+    }
+
+    *out = '\0';
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_extension: Get extension of filename passed in 'filename'.
+// Returned extension is always lower case.
+// Returns: 1 if ok, 0 if not.
+//-----------------------------------------------------------------------------
+int fatfs_get_extension(char* filename, char* out, int maxlen)
+{
+    int len = 0;
+
+    // Get files extension offset
+    int ext_pos = FileString_GetExtension(filename);
+
+    if (ext_pos > 0 && out && maxlen)
+    {
+        filename += ext_pos + 1;
+
+        while (*filename && len < (maxlen-1))
+        {
+            char a = *filename++;
+
+            // Make lowercase if uppercase
+            if ((a>='A') && (a<='Z'))
+                a+= 32;
+
+            *out++ = a;
+            len++;
+        }
+
+        *out = '\0';
+        return 1;
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_path_string: Append path & filename to create file path string.
+// Returns: 1 if ok, 0 if not.
+//-----------------------------------------------------------------------------
+int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen)
+{
+    int len = 0;
+    char last = 0;
+    char seperator = '/';
+
+    if (path && filename && out && maxlen > 0)
+    {
+        while (*path && len < (maxlen-2))
+        {
+            last = *path++;
+            if (last == '\\')
+                seperator = '\\';
+            *out++ = last;
+            len++;
+        }
+
+        // Add a seperator if trailing one not found
+        if (last != '\\' && last != '/')
+            *out++ = seperator;
+
+        while (*filename && len < (maxlen-1))
+        {
+            *out++ = *filename++;
+            len++;
+        }
+
+        *out = '\0';
+
+        return 1;
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// Test Bench
+//-----------------------------------------------------------------------------
+#ifdef FAT_STRING_TESTBENCH
+void main(void)
+{
+    char output[255];
+    char output2[255];
+
+    assert(fatfs_total_path_levels("C:\\folder\\file.zip") == 1);
+    assert(fatfs_total_path_levels("C:\\file.zip") == 0);
+    assert(fatfs_total_path_levels("C:\\folder\\folder2\\file.zip") == 2);
+    assert(fatfs_total_path_levels("C:\\") == -1);
+    assert(fatfs_total_path_levels("") == -1);
+    assert(fatfs_total_path_levels("/dev/etc/file.zip") == 2);
+    assert(fatfs_total_path_levels("/dev/file.zip") == 1);
+
+    assert(fatfs_get_substring("C:\\folder\\file.zip", 0, output, sizeof(output)) == 0);
+    assert(strcmp(output, "folder") == 0);
+
+    assert(fatfs_get_substring("C:\\folder\\file.zip", 1, output, sizeof(output)) == 0);
+    assert(strcmp(output, "file.zip") == 0);
+
+    assert(fatfs_get_substring("/dev/etc/file.zip", 0, output, sizeof(output)) == 0);
+    assert(strcmp(output, "dev") == 0);
+
+    assert(fatfs_get_substring("/dev/etc/file.zip", 1, output, sizeof(output)) == 0);
+    assert(strcmp(output, "etc") == 0);
+
+    assert(fatfs_get_substring("/dev/etc/file.zip", 2, output, sizeof(output)) == 0);
+    assert(strcmp(output, "file.zip") == 0);
+
+    assert(fatfs_split_path("C:\\folder\\file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+    assert(strcmp(output, "C:\\folder") == 0);
+    assert(strcmp(output2, "file.zip") == 0);
+
+    assert(fatfs_split_path("C:\\file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+    assert(output[0] == 0);
+    assert(strcmp(output2, "file.zip") == 0);
+
+    assert(fatfs_split_path("/dev/etc/file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+    assert(strcmp(output, "/dev/etc") == 0);
+    assert(strcmp(output2, "file.zip") == 0);
+
+    assert(FileString_GetExtension("C:\\file.zip") == strlen("C:\\file"));
+    assert(FileString_GetExtension("C:\\file.zip.ext") == strlen("C:\\file.zip"));
+    assert(FileString_GetExtension("C:\\file.zip.") == strlen("C:\\file.zip"));
+
+    assert(FileString_TrimLength("C:\\file.zip", strlen("C:\\file.zip")) == strlen("C:\\file.zip"));
+    assert(FileString_TrimLength("C:\\file.zip   ", strlen("C:\\file.zip   ")) == strlen("C:\\file.zip"));
+    assert(FileString_TrimLength("   ", strlen("   ")) == 0);
+
+    assert(fatfs_compare_names("C:\\file.ext", "C:\\file.ext") == 1);
+    assert(fatfs_compare_names("C:\\file2.ext", "C:\\file.ext") == 0);
+    assert(fatfs_compare_names("C:\\file  .ext", "C:\\file.ext") == 1);
+    assert(fatfs_compare_names("C:\\file  .ext", "C:\\file2.ext") == 0);
+
+    assert(fatfs_string_ends_with_slash("C:\\folder") == 0);
+    assert(fatfs_string_ends_with_slash("C:\\folder\\") == 1);
+    assert(fatfs_string_ends_with_slash("/path") == 0);
+    assert(fatfs_string_ends_with_slash("/path/a") == 0);
+    assert(fatfs_string_ends_with_slash("/path/") == 1);
+
+    assert(fatfs_get_extension("/mypath/file.wav", output, 4) == 1);
+    assert(strcmp(output, "wav") == 0);
+    assert(fatfs_get_extension("/mypath/file.WAV", output, 4) == 1);
+    assert(strcmp(output, "wav") == 0);
+    assert(fatfs_get_extension("/mypath/file.zip", output, 4) == 1);
+    assert(strcmp(output, "ext") != 0);
+
+    assert(fatfs_create_path_string("/mydir1", "myfile.txt", output, sizeof(output)) == 1);
+    assert(strcmp(output, "/mydir1/myfile.txt") == 0);
+    assert(fatfs_create_path_string("/mydir2/", "myfile2.txt", output, sizeof(output)) == 1);
+    assert(strcmp(output, "/mydir2/myfile2.txt") == 0);
+    assert(fatfs_create_path_string("C:\\mydir3", "myfile3.txt", output, sizeof(output)) == 1);
+    assert(strcmp(output, "C:\\mydir3\\myfile3.txt") == 0);
+}
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_string.h b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_string.h
new file mode 100644 (file)
index 0000000..90ca8e0
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __FILESTRING_H__
+#define __FILESTRING_H__
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_total_path_levels(char *path);
+int fatfs_get_substring(char *Path, int levelreq, char *output, int max_len);
+int fatfs_split_path(char *FullPath, char *Path, int max_path, char *FileName, int max_filename);
+int fatfs_compare_names(char* strA, char* strB);
+int fatfs_string_ends_with_slash(char *path);
+int fatfs_get_sfn_display_name(char* out, char* in);
+int fatfs_get_extension(char* filename, char* out, int maxlen);
+int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen);
+
+#ifndef NULL
+    #define NULL 0
+#endif
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_table.c b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_table.c
new file mode 100644 (file)
index 0000000..35dcc7b
--- /dev/null
@@ -0,0 +1,478 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//                            FAT16/32 File IO Library
+//                                    V2.6
+//                              Ultra-Embedded.com
+//                            Copyright 2003 - 2012
+//
+//                         Email: admin@ultra-embedded.com
+//
+//                                License: GPL
+//   If you would like a version with a more permissive license for use in
+//   closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+
+#ifndef FAT_BUFFERS
+    #define FAT_BUFFERS 1
+#endif
+
+#ifndef FAT_BUFFER_SECTORS
+    #define FAT_BUFFER_SECTORS 1
+#endif
+
+#if FAT_BUFFERS < 1 || FAT_BUFFER_SECTORS < 1
+    #error "FAT_BUFFERS & FAT_BUFFER_SECTORS must be at least 1"
+#endif
+
+//-----------------------------------------------------------------------------
+//                            FAT Sector Buffer
+//-----------------------------------------------------------------------------
+#define FAT32_GET_32BIT_WORD(pbuf, location)        ( GET_32BIT_WORD(pbuf->ptr, location) )
+#define FAT32_SET_32BIT_WORD(pbuf, location, value) { SET_32BIT_WORD(pbuf->ptr, location, value); pbuf->dirty = 1; }
+#define FAT16_GET_16BIT_WORD(pbuf, location)        ( GET_16BIT_WORD(pbuf->ptr, location) )
+#define FAT16_SET_16BIT_WORD(pbuf, location, value) { SET_16BIT_WORD(pbuf->ptr, location, value); pbuf->dirty = 1; }
+
+//-----------------------------------------------------------------------------
+// fatfs_fat_init:
+//-----------------------------------------------------------------------------
+void fatfs_fat_init(struct fatfs *fs)
+{
+    int i;
+
+    // FAT buffer chain head
+    fs->fat_buffer_head = NULL;
+
+    for (i=0;i<FAT_BUFFERS;i++)
+    {
+        // Initialise buffers to invalid
+        fs->fat_buffers[i].address = FAT32_INVALID_CLUSTER;
+        fs->fat_buffers[i].dirty = 0;
+        memset(fs->fat_buffers[i].sector, 0x00, sizeof(fs->fat_buffers[i].sector));
+        fs->fat_buffers[i].ptr = NULL;
+
+        // Add to head of queue
+        fs->fat_buffers[i].next = fs->fat_buffer_head;
+        fs->fat_buffer_head = &fs->fat_buffers[i];
+    }
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_writeback: Writeback 'dirty' FAT sectors to disk
+//-----------------------------------------------------------------------------
+static int fatfs_fat_writeback(struct fatfs *fs, struct fat_buffer *pcur)
+{
+    if (pcur)
+    {
+        // Writeback sector if changed
+        if (pcur->dirty)
+        {
+            if (fs->disk_io.write_media)
+            {
+                uint32 sectors = FAT_BUFFER_SECTORS;
+                uint32 offset = pcur->address - fs->fat_begin_lba;
+
+                // Limit to sectors used for the FAT
+                if ((offset + FAT_BUFFER_SECTORS) <= fs->fat_sectors)
+                    sectors = FAT_BUFFER_SECTORS;
+                else
+                    sectors = fs->fat_sectors - offset;
+
+                if (!fs->disk_io.write_media(pcur->address, pcur->sector, sectors))
+                    return 0;
+            }
+
+            pcur->dirty = 0;
+        }
+
+        return 1;
+    }
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_read_sector: Read a FAT sector
+//-----------------------------------------------------------------------------
+static struct fat_buffer *fatfs_fat_read_sector(struct fatfs *fs, uint32 sector)
+{
+    struct fat_buffer *last = NULL;
+    struct fat_buffer *pcur = fs->fat_buffer_head;
+
+    // Itterate through sector buffer list
+    while (pcur)
+    {
+        // Sector within this buffer?
+        if ((sector >= pcur->address) && (sector < (pcur->address + FAT_BUFFER_SECTORS)))
+            break;
+
+        // End of list?
+        if (pcur->next == NULL)
+        {
+            // Remove buffer from list
+            if (last)
+                last->next = NULL;
+            // We the first and last buffer in the chain?
+            else
+                fs->fat_buffer_head = NULL;
+        }
+
+        last = pcur;
+        pcur = pcur->next;
+    }
+
+    // We found the sector already in FAT buffer chain
+    if (pcur)
+    {
+        pcur->ptr = (uint8 *)(pcur->sector + ((sector - pcur->address) * FAT_SECTOR_SIZE));
+        return pcur;
+    }
+
+    // Else, we removed the last item from the list
+    pcur = last;
+
+    // Add to start of sector buffer list (now newest sector)
+    pcur->next = fs->fat_buffer_head;
+    fs->fat_buffer_head = pcur;
+
+    // Writeback sector if changed
+    if (pcur->dirty)
+        if (!fatfs_fat_writeback(fs, pcur))
+            return 0;
+
+    // Address is now new sector
+    pcur->address = sector;
+
+    // Read next sector
+    if (!fs->disk_io.read_media(pcur->address, pcur->sector, FAT_BUFFER_SECTORS))
+    {
+        // Read failed, invalidate buffer address
+        pcur->address = FAT32_INVALID_CLUSTER;
+        return NULL;
+    }
+
+    pcur->ptr = pcur->sector;
+    return pcur;
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_purge: Purge 'dirty' FAT sectors to disk
+//-----------------------------------------------------------------------------
+int fatfs_fat_purge(struct fatfs *fs)
+{
+    struct fat_buffer *pcur = fs->fat_buffer_head;
+
+    // Itterate through sector buffer list
+    while (pcur)
+    {
+        // Writeback sector if changed
+        if (pcur->dirty)
+            if (!fatfs_fat_writeback(fs, pcur))
+                return 0;
+
+        pcur = pcur->next;
+    }
+
+    return 1;
+}
+
+//-----------------------------------------------------------------------------
+//                        General FAT Table Operations
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// fatfs_find_next_cluster: Return cluster number of next cluster in chain by
+// reading FAT table and traversing it. Return 0xffffffff for end of chain.
+//-----------------------------------------------------------------------------
+uint32 fatfs_find_next_cluster(struct fatfs *fs, uint32 current_cluster)
+{
+    uint32 fat_sector_offset, position;
+    uint32 nextcluster;
+    struct fat_buffer *pbuf;
+
+    // Why is '..' labelled with cluster 0 when it should be 2 ??
+    if (current_cluster == 0)
+        current_cluster = 2;
+
+    // Find which sector of FAT table to read
+    if (fs->fat_type == FAT_TYPE_16)
+        fat_sector_offset = current_cluster / 256;
+    else
+        fat_sector_offset = current_cluster / 128;
+
+    // Read FAT sector into buffer
+    pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
+    if (!pbuf)
+        return (FAT32_LAST_CLUSTER);
+
+    if (fs->fat_type == FAT_TYPE_16)
+    {
+        // Find 32 bit entry of current sector relating to cluster number
+        position = (current_cluster - (fat_sector_offset * 256)) * 2;
+
+        // Read Next Clusters value from Sector Buffer
+        nextcluster = FAT16_GET_16BIT_WORD(pbuf, (uint16)position);
+
+        // If end of chain found
+        if (nextcluster >= 0xFFF8 && nextcluster <= 0xFFFF)
+            return (FAT32_LAST_CLUSTER);
+    }
+    else
+    {
+        // Find 32 bit entry of current sector relating to cluster number
+        position = (current_cluster - (fat_sector_offset * 128)) * 4;
+
+        // Read Next Clusters value from Sector Buffer
+        nextcluster = FAT32_GET_32BIT_WORD(pbuf, (uint16)position);
+
+        // Mask out MS 4 bits (its 28bit addressing)
+        nextcluster = nextcluster & 0x0FFFFFFF;
+
+        // If end of chain found
+        if (nextcluster >= 0x0FFFFFF8 && nextcluster <= 0x0FFFFFFF)
+            return (FAT32_LAST_CLUSTER);
+    }
+
+    // Else return next cluster
+    return (nextcluster);
+}
+//-----------------------------------------------------------------------------
+// fatfs_set_fs_info_next_free_cluster: Write the next free cluster to the FSINFO table
+//-----------------------------------------------------------------------------
+void fatfs_set_fs_info_next_free_cluster(struct fatfs *fs, uint32 newValue)
+{
+    if (fs->fat_type == FAT_TYPE_16)
+        ;
+    else
+    {
+        // Load sector to change it
+        struct fat_buffer *pbuf = fatfs_fat_read_sector(fs, fs->lba_begin+fs->fs_info_sector);
+        if (!pbuf)
+            return ;
+
+        // Change
+        FAT32_SET_32BIT_WORD(pbuf, 492, newValue);
+        fs->next_free_cluster = newValue;
+
+        // Write back FSINFO sector to disk
+        if (fs->disk_io.write_media)
+            fs->disk_io.write_media(pbuf->address, pbuf->sector, 1);
+
+        // Invalidate cache entry
+        pbuf->address = FAT32_INVALID_CLUSTER;
+        pbuf->dirty = 0;
+    }
+}
+//-----------------------------------------------------------------------------
+// fatfs_find_blank_cluster: Find a free cluster entry by reading the FAT
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_find_blank_cluster(struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster)
+{
+    uint32 fat_sector_offset, position;
+    uint32 nextcluster;
+    uint32 current_cluster = start_cluster;
+    struct fat_buffer *pbuf;
+
+    do
+    {
+        // Find which sector of FAT table to read
+        if (fs->fat_type == FAT_TYPE_16)
+            fat_sector_offset = current_cluster / 256;
+        else
+            fat_sector_offset = current_cluster / 128;
+
+        if ( fat_sector_offset < fs->fat_sectors)
+        {
+            // Read FAT sector into buffer
+            pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
+            if (!pbuf)
+                return 0;
+
+            if (fs->fat_type == FAT_TYPE_16)
+            {
+                // Find 32 bit entry of current sector relating to cluster number
+                position = (current_cluster - (fat_sector_offset * 256)) * 2;
+
+                // Read Next Clusters value from Sector Buffer
+                nextcluster = FAT16_GET_16BIT_WORD(pbuf, (uint16)position);
+            }
+            else
+            {
+                // Find 32 bit entry of current sector relating to cluster number
+                position = (current_cluster - (fat_sector_offset * 128)) * 4;
+
+                // Read Next Clusters value from Sector Buffer
+                nextcluster = FAT32_GET_32BIT_WORD(pbuf, (uint16)position);
+
+                // Mask out MS 4 bits (its 28bit addressing)
+                nextcluster = nextcluster & 0x0FFFFFFF;
+            }
+
+            if (nextcluster !=0 )
+                current_cluster++;
+        }
+        else
+            // Otherwise, run out of FAT sectors to check...
+            return 0;
+    }
+    while (nextcluster != 0x0);
+
+    // Found blank entry
+    *free_cluster = current_cluster;
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_fat_set_cluster: Set a cluster link in the chain. NOTE: Immediate
+// write (slow).
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_fat_set_cluster(struct fatfs *fs, uint32 cluster, uint32 next_cluster)
+{
+    struct fat_buffer *pbuf;
+    uint32 fat_sector_offset, position;
+
+    // Find which sector of FAT table to read
+    if (fs->fat_type == FAT_TYPE_16)
+        fat_sector_offset = cluster / 256;
+    else
+        fat_sector_offset = cluster / 128;
+
+    // Read FAT sector into buffer
+    pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
+    if (!pbuf)
+        return 0;
+
+    if (fs->fat_type == FAT_TYPE_16)
+    {
+        // Find 16 bit entry of current sector relating to cluster number
+        position = (cluster - (fat_sector_offset * 256)) * 2;
+
+        // Write Next Clusters value to Sector Buffer
+        FAT16_SET_16BIT_WORD(pbuf, (uint16)position, ((uint16)next_cluster));
+    }
+    else
+    {
+        // Find 32 bit entry of current sector relating to cluster number
+        position = (cluster - (fat_sector_offset * 128)) * 4;
+
+        // Write Next Clusters value to Sector Buffer
+        FAT32_SET_32BIT_WORD(pbuf, (uint16)position, next_cluster);
+    }
+
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_free_cluster_chain: Follow a chain marking each element as free
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_free_cluster_chain(struct fatfs *fs, uint32 start_cluster)
+{
+    uint32 last_cluster;
+    uint32 next_cluster = start_cluster;
+
+    // Loop until end of chain
+    while ( (next_cluster != FAT32_LAST_CLUSTER) && (next_cluster != 0x00000000) )
+    {
+        last_cluster = next_cluster;
+
+        // Find next link
+        next_cluster = fatfs_find_next_cluster(fs, next_cluster);
+
+        // Clear last link
+        fatfs_fat_set_cluster(fs, last_cluster, 0x00000000);
+    }
+
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_fat_add_cluster_to_chain: Follow a chain marking and then add a new entry
+// to the current tail.
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_fat_add_cluster_to_chain(struct fatfs *fs, uint32 start_cluster, uint32 newEntry)
+{
+    uint32 last_cluster = FAT32_LAST_CLUSTER;
+    uint32 next_cluster = start_cluster;
+
+    if (start_cluster == FAT32_LAST_CLUSTER)
+        return 0;
+
+    // Loop until end of chain
+    while ( next_cluster != FAT32_LAST_CLUSTER )
+    {
+        last_cluster = next_cluster;
+
+        // Find next link
+        next_cluster = fatfs_find_next_cluster(fs, next_cluster);
+        if (!next_cluster)
+            return 0;
+    }
+
+    // Add link in for new cluster
+    fatfs_fat_set_cluster(fs, last_cluster, newEntry);
+
+    // Mark new cluster as end of chain
+    fatfs_fat_set_cluster(fs, newEntry, FAT32_LAST_CLUSTER);
+
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_count_free_clusters:
+//-----------------------------------------------------------------------------
+uint32 fatfs_count_free_clusters(struct fatfs *fs)
+{
+    uint32 i,j;
+    uint32 count = 0;
+    struct fat_buffer *pbuf;
+
+    for (i = 0; i < fs->fat_sectors; i++)
+    {
+        // Read FAT sector into buffer
+        pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba + i);
+        if (!pbuf)
+            break;
+
+        for (j = 0; j < FAT_SECTOR_SIZE; )
+        {
+            if (fs->fat_type == FAT_TYPE_16)
+            {
+                if (FAT16_GET_16BIT_WORD(pbuf, (uint16)j) == 0)
+                    count++;
+
+                j += 2;
+            }
+            else
+            {
+                if (FAT32_GET_32BIT_WORD(pbuf, (uint16)j) == 0)
+                    count++;
+
+                j += 4;
+            }
+        }
+    }
+
+    return count;
+}
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_table.h b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_table.h
new file mode 100644 (file)
index 0000000..ead75f3
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __FAT_TABLE_H__
+#define __FAT_TABLE_H__
+
+#include "fat_opts.h"
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void    fatfs_fat_init(struct fatfs *fs);
+int     fatfs_fat_purge(struct fatfs *fs);
+uint32  fatfs_find_next_cluster(struct fatfs *fs, uint32 current_cluster);
+void    fatfs_set_fs_info_next_free_cluster(struct fatfs *fs, uint32 newValue);
+int     fatfs_find_blank_cluster(struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster);
+int     fatfs_fat_set_cluster(struct fatfs *fs, uint32 cluster, uint32 next_cluster);
+int     fatfs_fat_add_cluster_to_chain(struct fatfs *fs, uint32 start_cluster, uint32 newEntry);
+int     fatfs_free_cluster_chain(struct fatfs *fs, uint32 start_cluster);
+uint32  fatfs_count_free_clusters(struct fatfs *fs);
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_types.h b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_types.h
new file mode 100644 (file)
index 0000000..5e2cca8
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef __FAT_TYPES_H__
+#define __FAT_TYPES_H__
+
+// Detect 64-bit compilation on GCC
+#if defined(__GNUC__) && defined(__SIZEOF_LONG__)
+    #if __SIZEOF_LONG__ == 8
+        #define FATFS_DEF_UINT32_AS_INT
+    #endif
+#endif
+
+//-------------------------------------------------------------
+// System specific types
+//-------------------------------------------------------------
+#ifndef FATFS_NO_DEF_TYPES
+    typedef unsigned char uint8;
+    typedef unsigned short uint16;
+
+    // If compiling on a 64-bit machine, use int as 32-bits
+    #ifdef FATFS_DEF_UINT32_AS_INT
+        typedef unsigned int uint32;
+    // Else for 32-bit machines & embedded systems, use long...
+    #else
+        typedef unsigned long uint32;
+    #endif
+#endif
+
+#ifndef NULL
+    #define NULL 0
+#endif
+
+//-------------------------------------------------------------
+// Endian Macros
+//-------------------------------------------------------------
+// FAT is little endian so big endian systems need to swap words
+
+// Little Endian - No swap required
+#if FATFS_IS_LITTLE_ENDIAN == 1
+
+    #define FAT_HTONS(n) (n)
+    #define FAT_HTONL(n) (n)
+
+// Big Endian - Swap required
+#else
+
+    #define FAT_HTONS(n) ((((uint16)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))
+    #define FAT_HTONL(n) (((((uint32)(n) & 0xFF)) << 24) | \
+                    ((((uint32)(n) & 0xFF00)) << 8) | \
+                    ((((uint32)(n) & 0xFF0000)) >> 8) | \
+                    ((((uint32)(n) & 0xFF000000)) >> 24))
+
+#endif
+
+//-------------------------------------------------------------
+// Structure Packing Compile Options
+//-------------------------------------------------------------
+#ifdef __GNUC__
+    #define STRUCT_PACK
+    #define STRUCT_PACK_BEGIN
+    #define STRUCT_PACK_END
+    #define STRUCT_PACKED           __attribute__ ((packed))
+#else
+    // Other compilers may require other methods of packing structures
+    #define STRUCT_PACK
+    #define STRUCT_PACK_BEGIN
+    #define STRUCT_PACK_END
+    #define STRUCT_PACKED
+#endif
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_write.c b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_write.c
new file mode 100644 (file)
index 0000000..8b12c4b
--- /dev/null
@@ -0,0 +1,373 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//                            FAT16/32 File IO Library
+//                                    V2.6
+//                              Ultra-Embedded.com
+//                            Copyright 2003 - 2012
+//
+//                         Email: admin@ultra-embedded.com
+//
+//                                License: GPL
+//   If you would like a version with a more permissive license for use in
+//   closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+
+#if FATFS_INC_WRITE_SUPPORT
+//-----------------------------------------------------------------------------
+// fatfs_add_free_space: Allocate another cluster of free space to the end
+// of a files cluster chain.
+//-----------------------------------------------------------------------------
+int fatfs_add_free_space(struct fatfs *fs, uint32 *startCluster, uint32 clusters)
+{
+    uint32 i;
+    uint32 nextcluster;
+    uint32 start = *startCluster;
+
+    // Set the next free cluster hint to unknown
+    if (fs->next_free_cluster != FAT32_LAST_CLUSTER)
+        fatfs_set_fs_info_next_free_cluster(fs, FAT32_LAST_CLUSTER);
+
+    for (i=0;i<clusters;i++)
+    {
+        // Start looking for free clusters from the beginning
+        if (fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &nextcluster))
+        {
+            // Point last to this
+            fatfs_fat_set_cluster(fs, start, nextcluster);
+
+            // Point this to end of file
+            fatfs_fat_set_cluster(fs, nextcluster, FAT32_LAST_CLUSTER);
+
+            // Adjust argument reference
+            start = nextcluster;
+            if (i == 0)
+                *startCluster = nextcluster;
+        }
+        else
+            return 0;
+    }
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_allocate_free_space: Add an ammount of free space to a file either from
+// 'startCluster' if newFile = false, or allocating a new start to the chain if
+// newFile = true.
+//-----------------------------------------------------------------------------
+int fatfs_allocate_free_space(struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size)
+{
+    uint32 clusterSize;
+    uint32 clusterCount;
+    uint32 nextcluster;
+
+    if (size==0)
+        return 0;
+
+    // Set the next free cluster hint to unknown
+    if (fs->next_free_cluster != FAT32_LAST_CLUSTER)
+        fatfs_set_fs_info_next_free_cluster(fs, FAT32_LAST_CLUSTER);
+
+    // Work out size and clusters
+    clusterSize = fs->sectors_per_cluster * FAT_SECTOR_SIZE;
+    clusterCount = (size / clusterSize);
+
+    // If any left over
+    if (size-(clusterSize*clusterCount))
+        clusterCount++;
+
+    // Allocated first link in the chain if a new file
+    if (newFile)
+    {
+        if (!fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &nextcluster))
+            return 0;
+
+        // If this is all that is needed then all done
+        if (clusterCount==1)
+        {
+            fatfs_fat_set_cluster(fs, nextcluster, FAT32_LAST_CLUSTER);
+            *startCluster = nextcluster;
+            return 1;
+        }
+    }
+    // Allocate from end of current chain (startCluster is end of chain)
+    else
+        nextcluster = *startCluster;
+
+    if (!fatfs_add_free_space(fs, &nextcluster, clusterCount))
+            return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_find_free_dir_offset: Find a free space in the directory for a new entry
+// which takes up 'entryCount' blocks (or allocate some more)
+//-----------------------------------------------------------------------------
+static int fatfs_find_free_dir_offset(struct fatfs *fs, uint32 dirCluster, int entryCount, uint32 *pSector, uint8 *pOffset)
+{
+    struct fat_dir_entry *directoryEntry;
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    uint8 i=0;
+    int x=0;
+    int possible_spaces = 0;
+    int start_recorded = 0;
+
+    // No entries required?
+    if (entryCount == 0)
+        return 0;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, dirCluster, x++, 0))
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+                // LFN Entry
+                if (fatfs_entry_lfn_text(directoryEntry))
+                {
+                    // First entry?
+                    if (possible_spaces == 0)
+                    {
+                        // Store start
+                        *pSector = x-1;
+                        *pOffset = item;
+                        start_recorded = 1;
+                    }
+
+                    // Increment the count in-case the file turns
+                    // out to be deleted...
+                    possible_spaces++;
+                }
+                // SFN Entry
+                else
+                {
+                    // Has file been deleted?
+                    if (fs->currentsector.sector[recordoffset] == FILE_HEADER_DELETED)
+                    {
+                        // First entry?
+                        if (possible_spaces == 0)
+                        {
+                            // Store start
+                            *pSector = x-1;
+                            *pOffset = item;
+                            start_recorded = 1;
+                        }
+
+                        possible_spaces++;
+
+                        // We have found enough space?
+                        if (possible_spaces >= entryCount)
+                            return 1;
+
+                        // Else continue counting until we find a valid entry!
+                    }
+                    // Is the file entry empty?
+                    else if (fs->currentsector.sector[recordoffset] == FILE_HEADER_BLANK)
+                    {
+                        // First entry?
+                        if (possible_spaces == 0)
+                        {
+                            // Store start
+                            *pSector = x-1;
+                            *pOffset = item;
+                            start_recorded = 1;
+                        }
+
+                        // Increment the blank entries count
+                        possible_spaces++;
+
+                        // We have found enough space?
+                        if (possible_spaces >= entryCount)
+                            return 1;
+                    }
+                    // File entry is valid
+                    else
+                    {
+                        // Reset all flags
+                        possible_spaces = 0;
+                        start_recorded = 0;
+                    }
+                }
+            } // End of for
+        } // End of if
+        // Run out of free space in the directory, allocate some more
+        else
+        {
+            uint32 newCluster;
+
+            // Get a new cluster for directory
+            if (!fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &newCluster))
+                return 0;
+
+            // Add cluster to end of directory tree
+            if (!fatfs_fat_add_cluster_to_chain(fs, dirCluster, newCluster))
+                return 0;
+
+            // Erase new directory cluster
+            memset(fs->currentsector.sector, 0x00, FAT_SECTOR_SIZE);
+            for (i=0;i<fs->sectors_per_cluster;i++)
+            {
+                if (!fatfs_write_sector(fs, newCluster, i, 0))
+                    return 0;
+            }
+
+            // If non of the name fitted on previous sectors
+            if (!start_recorded)
+            {
+                // Store start
+                *pSector = (x-1);
+                *pOffset = 0;
+                start_recorded = 1;
+            }
+
+            return 1;
+        }
+    } // End of while loop
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_add_file_entry: Add a directory entry to a location found by FindFreeOffset
+//-----------------------------------------------------------------------------
+int fatfs_add_file_entry(struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    uint8 i=0;
+    uint32 x=0;
+    int entryCount;
+    struct fat_dir_entry shortEntry;
+    int dirtySector = 0;
+
+    uint32 dirSector = 0;
+    uint8 dirOffset = 0;
+    int foundEnd = 0;
+
+    uint8 checksum;
+    uint8 *pSname;
+
+    // No write access?
+    if (!fs->disk_io.write_media)
+        return 0;
+
+#if FATFS_INC_LFN_SUPPORT
+    // How many LFN entries are required?
+    // NOTE: We always request one LFN even if it would fit in a SFN!
+    entryCount = fatfs_lfn_entries_required(filename);
+    if (!entryCount)
+        return 0;
+#else
+    entryCount = 0;
+#endif
+
+    // Find space in the directory for this filename (or allocate some more)
+    // NOTE: We need to find space for at least the LFN + SFN (or just the SFN if LFNs not supported).
+    if (!fatfs_find_free_dir_offset(fs, dirCluster, entryCount + 1, &dirSector, &dirOffset))
+        return 0;
+
+    // Generate checksum of short filename
+    pSname = (uint8*)shortfilename;
+    checksum = 0;
+    for (i=11; i!=0; i--) checksum = ((checksum & 1) ? 0x80 : 0) + (checksum >> 1) + *pSname++;
+
+    // Start from current sector where space was found!
+    x = dirSector;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, dirCluster, x++, 0))
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // If the start position for the entry has been found
+                if (foundEnd==0)
+                    if ( (dirSector==(x-1)) && (dirOffset==item) )
+                        foundEnd = 1;
+
+                // Start adding filename
+                if (foundEnd)
+                {
+                    if (entryCount==0)
+                    {
+                        // Short filename
+                        fatfs_sfn_create_entry(shortfilename, size, startCluster, &shortEntry, dir);
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                        // Update create, access & modify time & date
+                        fatfs_update_timestamps(&shortEntry, 1, 1, 1);
+#endif
+
+                        memcpy(&fs->currentsector.sector[recordoffset], &shortEntry, sizeof(shortEntry));
+
+                        // Writeback
+                        return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+                    }
+#if FATFS_INC_LFN_SUPPORT
+                    else
+                    {
+                        entryCount--;
+
+                        // Copy entry to directory buffer
+                        fatfs_filename_to_lfn(filename, &fs->currentsector.sector[recordoffset], entryCount, checksum);
+                        dirtySector = 1;
+                    }
+#endif
+                }
+            } // End of if
+
+            // Write back to disk before loading another sector
+            if (dirtySector)
+            {
+                if (!fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1))
+                    return 0;
+
+                dirtySector = 0;
+            }
+        }
+        else
+            return 0;
+    } // End of while loop
+
+    return 0;
+}
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_write.h b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/fat_write.h
new file mode 100644 (file)
index 0000000..5558a86
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef __FAT_WRITE_H__
+#define __FAT_WRITE_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_add_file_entry(struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir);
+int fatfs_add_free_space(struct fatfs *fs, uint32 *startCluster, uint32 clusters);
+int fatfs_allocate_free_space(struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size);
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/fat_io_lib/version.txt b/Ventoy2Disk/Ventoy2Disk/fat_io_lib/version.txt
new file mode 100644 (file)
index 0000000..bc02b86
--- /dev/null
@@ -0,0 +1 @@
+2.6.11
diff --git a/Ventoy2Disk/Ventoy2Disk/ff14/LICENSE.txt b/Ventoy2Disk/Ventoy2Disk/ff14/LICENSE.txt
new file mode 100644 (file)
index 0000000..467e2a2
--- /dev/null
@@ -0,0 +1,24 @@
+FatFs License
+
+FatFs has being developped as a personal project of the author, ChaN. It is free from the code anyone else wrote at current release. Following code block shows a copy of the FatFs license document that heading the source files.
+
+/*----------------------------------------------------------------------------/
+/  FatFs - Generic FAT Filesystem Module  Rx.xx                               /
+/-----------------------------------------------------------------------------/
+/
+/ Copyright (C) 20xx, ChaN, all right reserved.
+/
+/ FatFs module is an open source software. Redistribution and use of FatFs in
+/ source and binary forms, with or without modification, are permitted provided
+/ that the following condition is met:
+/
+/ 1. Redistributions of source code must retain the above copyright notice,
+/    this condition and the following disclaimer.
+/
+/ This software is provided by the copyright holder and contributors "AS IS"
+/ and any warranties related to this software are DISCLAIMED.
+/ The copyright owner or contributors be NOT LIABLE for any damages caused
+/ by use of this software.
+/----------------------------------------------------------------------------*/
+
+Therefore FatFs license is one of the BSD-style licenses but there is a significant feature. FatFs is mainly intended for embedded systems. In order to extend the usability for commercial products, the redistributions of FatFs in binary form, such as embedded code, binary library and any forms without source code, does not need to include about FatFs in the documentations. This is equivalent to the 1-clause BSD license. Of course FatFs is compatible with the most of open source software licenses including GNU GPL. When you redistribute the FatFs source code with any changes or create a fork, the license can also be changed to GNU GPL, BSD-style license or any open source software license that not conflict with FatFs license.
diff --git a/Ventoy2Disk/Ventoy2Disk/ff14/source/00history.txt b/Ventoy2Disk/Ventoy2Disk/ff14/source/00history.txt
new file mode 100644 (file)
index 0000000..cb8753b
--- /dev/null
@@ -0,0 +1,340 @@
+----------------------------------------------------------------------------
+  Revision history of FatFs module
+----------------------------------------------------------------------------
+
+R0.00 (February 26, 2006)
+
+  Prototype.
+
+
+
+R0.01 (April 29, 2006)
+
+  The first release.
+
+
+
+R0.02 (June 01, 2006)
+
+  Added FAT12 support.
+  Removed unbuffered mode.
+  Fixed a problem on small (<32M) partition.
+
+
+
+R0.02a (June 10, 2006)
+
+  Added a configuration option (_FS_MINIMUM).
+
+
+
+R0.03 (September 22, 2006)
+
+  Added f_rename().
+  Changed option _FS_MINIMUM to _FS_MINIMIZE.
+
+
+
+R0.03a (December 11, 2006)
+
+  Improved cluster scan algorithm to write files fast.
+  Fixed f_mkdir() creates incorrect directory on FAT32.
+
+
+
+R0.04 (February 04, 2007)
+
+  Added f_mkfs().
+  Supported multiple drive system.
+  Changed some interfaces for multiple drive system.
+  Changed f_mountdrv() to f_mount().
+
+
+
+R0.04a (April 01, 2007)
+
+  Supported multiple partitions on a physical drive.
+  Added a capability of extending file size to f_lseek().
+  Added minimization level 3.
+  Fixed an endian sensitive code in f_mkfs().
+
+
+
+R0.04b (May 05, 2007)
+
+  Added a configuration option _USE_NTFLAG.
+  Added FSINFO support.
+  Fixed DBCS name can result FR_INVALID_NAME.
+  Fixed short seek (<= csize) collapses the file object.
+
+
+
+R0.05 (August 25, 2007)
+
+  Changed arguments of f_read(), f_write() and f_mkfs().
+  Fixed f_mkfs() on FAT32 creates incorrect FSINFO.
+  Fixed f_mkdir() on FAT32 creates incorrect directory.
+
+
+
+R0.05a (February 03, 2008)
+
+  Added f_truncate() and f_utime().
+  Fixed off by one error at FAT sub-type determination.
+  Fixed btr in f_read() can be mistruncated.
+  Fixed cached sector is not flushed when create and close without write.
+
+
+
+R0.06 (April 01, 2008)
+
+  Added fputc(), fputs(), fprintf() and fgets().
+  Improved performance of f_lseek() on moving to the same or following cluster.
+
+
+
+R0.07 (April 01, 2009)
+
+  Merged Tiny-FatFs as a configuration option. (_FS_TINY)
+  Added long file name feature. (_USE_LFN)
+  Added multiple code page feature. (_CODE_PAGE)
+  Added re-entrancy for multitask operation. (_FS_REENTRANT)
+  Added auto cluster size selection to f_mkfs().
+  Added rewind option to f_readdir().
+  Changed result code of critical errors.
+  Renamed string functions to avoid name collision.
+
+
+
+R0.07a (April 14, 2009)
+
+  Septemberarated out OS dependent code on reentrant cfg.
+  Added multiple sector size feature.
+
+
+
+R0.07c (June 21, 2009)
+
+  Fixed f_unlink() can return FR_OK on error.
+  Fixed wrong cache control in f_lseek().
+  Added relative path feature.
+  Added f_chdir() and f_chdrive().
+  Added proper case conversion to extended character.
+
+
+
+R0.07e (November 03, 2009)
+
+  Septemberarated out configuration options from ff.h to ffconf.h.
+  Fixed f_unlink() fails to remove a sub-directory on _FS_RPATH.
+  Fixed name matching error on the 13 character boundary.
+  Added a configuration option, _LFN_UNICODE.
+  Changed f_readdir() to return the SFN with always upper case on non-LFN cfg.
+
+
+
+R0.08 (May 15, 2010)
+
+  Added a memory configuration option. (_USE_LFN = 3)
+  Added file lock feature. (_FS_SHARE)
+  Added fast seek feature. (_USE_FASTSEEK)
+  Changed some types on the API, XCHAR->TCHAR.
+  Changed .fname in the FILINFO structure on Unicode cfg.
+  String functions support UTF-8 encoding files on Unicode cfg.
+
+
+
+R0.08a (August 16, 2010)
+
+  Added f_getcwd(). (_FS_RPATH = 2)
+  Added sector erase feature. (_USE_ERASE)
+  Moved file lock semaphore table from fs object to the bss.
+  Fixed f_mkfs() creates wrong FAT32 volume.
+
+
+
+R0.08b (January 15, 2011)
+
+  Fast seek feature is also applied to f_read() and f_write().
+  f_lseek() reports required table size on creating CLMP.
+  Extended format syntax of f_printf().
+  Ignores duplicated directory separators in given path name.
+
+
+
+R0.09 (September 06, 2011)
+
+  f_mkfs() supports multiple partition to complete the multiple partition feature.
+  Added f_fdisk().
+
+
+
+R0.09a (August 27, 2012)
+
+  Changed f_open() and f_opendir() reject null object pointer to avoid crash.
+  Changed option name _FS_SHARE to _FS_LOCK.
+  Fixed assertion failure due to OS/2 EA on FAT12/16 volume.
+
+
+
+R0.09b (January 24, 2013)
+
+  Added f_setlabel() and f_getlabel().
+
+
+
+R0.10 (October 02, 2013)
+
+  Added selection of character encoding on the file. (_STRF_ENCODE)
+  Added f_closedir().
+  Added forced full FAT scan for f_getfree(). (_FS_NOFSINFO)
+  Added forced mount feature with changes of f_mount().
+  Improved behavior of volume auto detection.
+  Improved write throughput of f_puts() and f_printf().
+  Changed argument of f_chdrive(), f_mkfs(), disk_read() and disk_write().
+  Fixed f_write() can be truncated when the file size is close to 4GB.
+  Fixed f_open(), f_mkdir() and f_setlabel() can return incorrect value on error.
+
+
+
+R0.10a (January 15, 2014)
+
+  Added arbitrary strings as drive number in the path name. (_STR_VOLUME_ID)
+  Added a configuration option of minimum sector size. (_MIN_SS)
+  2nd argument of f_rename() can have a drive number and it will be ignored.
+  Fixed f_mount() with forced mount fails when drive number is >= 1. (appeared at R0.10)
+  Fixed f_close() invalidates the file object without volume lock.
+  Fixed f_closedir() returns but the volume lock is left acquired. (appeared at R0.10)
+  Fixed creation of an entry with LFN fails on too many SFN collisions. (appeared at R0.07)
+
+
+
+R0.10b (May 19, 2014)
+
+  Fixed a hard error in the disk I/O layer can collapse the directory entry.
+  Fixed LFN entry is not deleted when delete/rename an object with lossy converted SFN. (appeared at R0.07)
+
+
+
+R0.10c (November 09, 2014)
+
+  Added a configuration option for the platforms without RTC. (_FS_NORTC)
+  Changed option name _USE_ERASE to _USE_TRIM.
+  Fixed volume label created by Mac OS X cannot be retrieved with f_getlabel(). (appeared at R0.09b)
+  Fixed a potential problem of FAT access that can appear on disk error.
+  Fixed null pointer dereference on attempting to delete the root direcotry. (appeared at R0.08)
+
+
+
+R0.11 (February 09, 2015)
+
+  Added f_findfirst(), f_findnext() and f_findclose(). (_USE_FIND)
+  Fixed f_unlink() does not remove cluster chain of the file. (appeared at R0.10c)
+  Fixed _FS_NORTC option does not work properly. (appeared at R0.10c)
+
+
+
+R0.11a (September 05, 2015)
+
+  Fixed wrong media change can lead a deadlock at thread-safe configuration.
+  Added code page 771, 860, 861, 863, 864, 865 and 869. (_CODE_PAGE)
+  Removed some code pages actually not exist on the standard systems. (_CODE_PAGE)
+  Fixed errors in the case conversion teble of code page 437 and 850 (ff.c).
+  Fixed errors in the case conversion teble of Unicode (cc*.c).
+
+
+
+R0.12 (April 12, 2016)
+
+  Added support for exFAT file system. (_FS_EXFAT)
+  Added f_expand(). (_USE_EXPAND)
+  Changed some members in FINFO structure and behavior of f_readdir().
+  Added an option _USE_CHMOD.
+  Removed an option _WORD_ACCESS.
+  Fixed errors in the case conversion table of Unicode (cc*.c).
+
+
+
+R0.12a (July 10, 2016)
+
+  Added support for creating exFAT volume with some changes of f_mkfs().
+  Added a file open method FA_OPEN_APPEND. An f_lseek() following f_open() is no longer needed.
+  f_forward() is available regardless of _FS_TINY.
+  Fixed f_mkfs() creates wrong volume. (appeared at R0.12)
+  Fixed wrong memory read in create_name(). (appeared at R0.12)
+  Fixed compilation fails at some configurations, _USE_FASTSEEK and _USE_FORWARD.
+
+
+
+R0.12b (September 04, 2016)
+
+  Made f_rename() be able to rename objects with the same name but case.
+  Fixed an error in the case conversion teble of code page 866. (ff.c)
+  Fixed writing data is truncated at the file offset 4GiB on the exFAT volume. (appeared at R0.12)
+  Fixed creating a file in the root directory of exFAT volume can fail. (appeared at R0.12)
+  Fixed f_mkfs() creating exFAT volume with too small cluster size can collapse unallocated memory. (appeared at R0.12)
+  Fixed wrong object name can be returned when read directory at Unicode cfg. (appeared at R0.12)
+  Fixed large file allocation/removing on the exFAT volume collapses allocation bitmap. (appeared at R0.12)
+  Fixed some internal errors in f_expand() and f_lseek(). (appeared at R0.12)
+
+
+
+R0.12c (March 04, 2017)
+
+  Improved write throughput at the fragmented file on the exFAT volume.
+  Made memory usage for exFAT be able to be reduced as decreasing _MAX_LFN.
+  Fixed successive f_getfree() can return wrong count on the FAT12/16 volume. (appeared at R0.12)
+  Fixed configuration option _VOLUMES cannot be set 10. (appeared at R0.10c)
+
+
+
+R0.13 (May 21, 2017)
+
+  Changed heading character of configuration keywords "_" to "FF_".
+  Removed ASCII-only configuration, FF_CODE_PAGE = 1. Use FF_CODE_PAGE = 437 instead.
+  Added f_setcp(), run-time code page configuration. (FF_CODE_PAGE = 0)
+  Improved cluster allocation time on stretch a deep buried cluster chain.
+  Improved processing time of f_mkdir() with large cluster size by using FF_USE_LFN = 3.
+  Improved NoFatChain flag of the fragmented file to be set after it is truncated and got contiguous.
+  Fixed archive attribute is left not set when a file on the exFAT volume is renamed. (appeared at R0.12)
+  Fixed exFAT FAT entry can be collapsed when write or lseek operation to the existing file is done. (appeared at R0.12c)
+  Fixed creating a file can fail when a new cluster allocation to the exFAT directory occures. (appeared at R0.12c)
+
+
+
+R0.13a (October 14, 2017)
+
+  Added support for UTF-8 encoding on the API. (FF_LFN_UNICODE = 2)
+  Added options for file name output buffer. (FF_LFN_BUF, FF_SFN_BUF).
+  Added dynamic memory allocation option for working buffer of f_mkfs() and f_fdisk().
+  Fixed f_fdisk() and f_mkfs() create the partition table with wrong CHS parameters. (appeared at R0.09)
+  Fixed f_unlink() can cause lost clusters at fragmented file on the exFAT volume. (appeared at R0.12c)
+  Fixed f_setlabel() rejects some valid characters for exFAT volume. (appeared at R0.12)
+
+
+
+R0.13b (April 07, 2018)
+
+  Added support for UTF-32 encoding on the API. (FF_LFN_UNICODE = 3)
+  Added support for Unix style volume ID. (FF_STR_VOLUME_ID = 2)
+  Fixed accesing any object on the exFAT root directory beyond the cluster boundary can fail. (appeared at R0.12c)
+  Fixed f_setlabel() does not reject some invalid characters. (appeared at R0.09b)
+
+
+
+R0.13c (October 14, 2018)
+  Supported stdint.h for C99 and later. (integer.h was included in ff.h)
+  Fixed reading a directory gets infinite loop when the last directory entry is not empty. (appeared at R0.12)
+  Fixed creating a sub-directory in the fragmented sub-directory on the exFAT volume collapses FAT chain of the parent directory. (appeared at R0.12)
+  Fixed f_getcwd() cause output buffer overrun when the buffer has a valid drive number. (appeared at R0.13b)
+
+
+
+R0.14 (October 14, 2019)
+  Added support for 64-bit LBA and GUID partition table (FF_LBA64 = 1)
+  Changed some API functions, f_mkfs() and f_fdisk().
+  Fixed f_open() function cannot find the file with file name in length of FF_MAX_LFN characters.
+  Fixed f_readdir() function cannot retrieve long file names in length of FF_MAX_LFN - 1 characters.
+  Fixed f_readdir() function returns file names with wrong case conversion. (appeared at R0.12)
+  Fixed f_mkfs() function can fail to create exFAT volume in the second partition. (appeared at R0.12)
+
diff --git a/Ventoy2Disk/Ventoy2Disk/ff14/source/00readme.txt b/Ventoy2Disk/Ventoy2Disk/ff14/source/00readme.txt
new file mode 100644 (file)
index 0000000..234c675
--- /dev/null
@@ -0,0 +1,21 @@
+FatFs Module Source Files R0.14
+
+
+FILES
+
+  00readme.txt   This file.
+  00history.txt  Revision history.
+  ff.c           FatFs module.
+  ffconf.h       Configuration file of FatFs module.
+  ff.h           Common include file for FatFs and application module.
+  diskio.h       Common include file for FatFs and disk I/O module.
+  diskio.c       An example of glue function to attach existing disk I/O module to FatFs.
+  ffunicode.c    Optional Unicode utility functions.
+  ffsystem.c     An example of optional O/S related functions.
+
+
+  Low level disk I/O module is not included in this archive because the FatFs
+  module is only a generic file system layer and it does not depend on any specific
+  storage device. You need to provide a low level disk I/O module written to
+  control the storage device that attached to the target system.
+
diff --git a/Ventoy2Disk/Ventoy2Disk/ff14/source/diskio.c b/Ventoy2Disk/Ventoy2Disk/ff14/source/diskio.c
new file mode 100644 (file)
index 0000000..bebe625
--- /dev/null
@@ -0,0 +1,259 @@
+/*-----------------------------------------------------------------------*/
+/* Low level disk I/O module skeleton for FatFs     (C)ChaN, 2019        */
+/*-----------------------------------------------------------------------*/
+/* If a working storage control module is available, it should be        */
+/* attached to the FatFs via a glue function rather than modifying it.   */
+/* This is an example of glue functions to attach various exsisting      */
+/* storage control modules to the FatFs module with a defined API.       */
+/*-----------------------------------------------------------------------*/
+
+#include <Windows.h>
+
+#include "ff.h"                        /* Obtains integer types */
+#include "diskio.h"            /* Declarations of disk functions */
+
+/* Definitions of physical drive number for each drive */
+#define DEV_RAM                0       /* Example: Map Ramdisk to physical drive 0 */
+#define DEV_MMC                1       /* Example: Map MMC/SD card to physical drive 1 */
+#define DEV_USB                2       /* Example: Map USB MSD to physical drive 2 */
+
+void Log(const char *fmt, ...);
+
+static UINT8 g_MbrSector[512];
+HANDLE g_hPhyDrive;
+UINT64 g_SectorCount;
+
+void disk_io_set_param(HANDLE Handle, UINT64 SectorCount)
+{
+    g_hPhyDrive = Handle;
+    g_SectorCount = SectorCount;
+}
+
+/*-----------------------------------------------------------------------*/
+/* Get Drive Status                                                      */
+/*-----------------------------------------------------------------------*/
+
+DSTATUS disk_status (
+       BYTE pdrv               /* Physical drive nmuber to identify the drive */
+)
+{
+    return RES_OK;
+#if 0
+       DSTATUS stat;
+       int result;
+
+       switch (pdrv) {
+       case DEV_RAM :
+               result = RAM_disk_status();
+
+               // translate the reslut code here
+
+               return stat;
+
+       case DEV_MMC :
+               result = MMC_disk_status();
+
+               // translate the reslut code here
+
+               return stat;
+
+       case DEV_USB :
+               result = USB_disk_status();
+
+               // translate the reslut code here
+
+               return stat;
+       }
+       return STA_NOINIT;
+#endif
+}
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Inidialize a Drive                                                    */
+/*-----------------------------------------------------------------------*/
+
+DSTATUS disk_initialize (
+       BYTE pdrv                               /* Physical drive nmuber to identify the drive */
+)
+{
+    return RES_OK;
+#if 0
+       DSTATUS stat;
+       int result;
+
+       switch (pdrv) {
+       case DEV_RAM :
+               result = RAM_disk_initialize();
+
+               // translate the reslut code here
+
+               return stat;
+
+       case DEV_MMC :
+               result = MMC_disk_initialize();
+
+               // translate the reslut code here
+
+               return stat;
+
+       case DEV_USB :
+               result = USB_disk_initialize();
+
+               // translate the reslut code here
+
+               return stat;
+       }
+       return STA_NOINIT;
+#endif
+}
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read Sector(s)                                                        */
+/*-----------------------------------------------------------------------*/
+
+DRESULT disk_read (
+       BYTE pdrv,              /* Physical drive nmuber to identify the drive */
+       BYTE *buff,             /* Data buffer to store read data */
+       LBA_t sector,   /* Start sector in LBA */
+       UINT count              /* Number of sectors to read */
+)
+{
+    DWORD dwSize;
+    BOOL bRet;
+    LARGE_INTEGER liCurrentPosition;
+
+    liCurrentPosition.QuadPart = sector * 512;
+    SetFilePointerEx(g_hPhyDrive, liCurrentPosition, &liCurrentPosition, FILE_BEGIN);
+
+    bRet = ReadFile(g_hPhyDrive, buff, count * 512, &dwSize, NULL);
+
+    if (dwSize != count * 512)
+    {
+        Log("ReadFile error bRet:%u WriteSize:%u dwSize:%u ErrCode:%u", bRet, count * 512, dwSize, GetLastError());
+    }
+
+    if (sector == 0)
+    {
+        memcpy(buff, g_MbrSector, sizeof(g_MbrSector));
+    }
+
+    return RES_OK;
+}
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Write Sector(s)                                                       */
+/*-----------------------------------------------------------------------*/
+
+#if FF_FS_READONLY == 0
+
+DRESULT disk_write (
+       BYTE pdrv,                      /* Physical drive nmuber to identify the drive */
+       const BYTE *buff,       /* Data to be written */
+       LBA_t sector,           /* Start sector in LBA */
+       UINT count                      /* Number of sectors to write */
+)
+{
+    DWORD dwSize;
+    BOOL bRet;
+    LARGE_INTEGER liCurrentPosition;
+
+    // skip MBR
+    if (sector == 0)
+    {
+        memcpy(g_MbrSector, buff, sizeof(g_MbrSector));
+
+        if (count == 1)
+        {
+            return RES_OK;
+        }
+
+        sector++;
+        count--;
+    }
+
+    liCurrentPosition.QuadPart = sector * 512;
+    SetFilePointerEx(g_hPhyDrive, liCurrentPosition, &liCurrentPosition, FILE_BEGIN);
+
+    bRet = WriteFile(g_hPhyDrive, buff, count * 512, &dwSize, NULL);
+
+    if (dwSize != count * 512)
+    {
+        Log("WriteFile error bRet:%u WriteSize:%u dwSize:%u ErrCode:%u", bRet, count * 512, dwSize, GetLastError());
+    }
+
+    return RES_OK;
+}
+
+#endif
+
+
+/*-----------------------------------------------------------------------*/
+/* Miscellaneous Functions                                               */
+/*-----------------------------------------------------------------------*/
+
+DRESULT disk_ioctl (
+       BYTE pdrv,              /* Physical drive nmuber (0..) */
+       BYTE cmd,               /* Control code */
+       void *buff              /* Buffer to send/receive control data */
+)
+{
+    switch (cmd)
+    {
+        case CTRL_SYNC:
+        {
+            //FILE_FLAG_NO_BUFFERING & FILE_FLAG_WRITE_THROUGH was set, no need to sync
+            break;
+        }
+        case GET_SECTOR_COUNT:
+        {
+            *(LBA_t *)buff = g_SectorCount;
+            break;
+        }
+        case GET_SECTOR_SIZE:
+        {
+            *(WORD *)buff = 512;
+            break;
+        }
+        case GET_BLOCK_SIZE:
+        {
+            *(DWORD *)buff = 8;
+            break;
+        }
+    }
+
+    return RES_OK;
+
+#if 0
+       DRESULT res;
+       int result;
+
+       switch (pdrv) {
+       case DEV_RAM :
+
+               // Process of the command for the RAM drive
+
+               return res;
+
+       case DEV_MMC :
+
+               // Process of the command for the MMC/SD card
+
+               return res;
+
+       case DEV_USB :
+
+               // Process of the command the USB drive
+
+               return res;
+       }
+
+       return RES_PARERR;
+#endif
+}
+
diff --git a/Ventoy2Disk/Ventoy2Disk/ff14/source/diskio.h b/Ventoy2Disk/Ventoy2Disk/ff14/source/diskio.h
new file mode 100644 (file)
index 0000000..e4ead78
--- /dev/null
@@ -0,0 +1,77 @@
+/*-----------------------------------------------------------------------/
+/  Low level disk interface modlue include file   (C)ChaN, 2019          /
+/-----------------------------------------------------------------------*/
+
+#ifndef _DISKIO_DEFINED
+#define _DISKIO_DEFINED
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* Status of Disk Functions */
+typedef BYTE   DSTATUS;
+
+/* Results of Disk Functions */
+typedef enum {
+       RES_OK = 0,             /* 0: Successful */
+       RES_ERROR,              /* 1: R/W Error */
+       RES_WRPRT,              /* 2: Write Protected */
+       RES_NOTRDY,             /* 3: Not Ready */
+       RES_PARERR              /* 4: Invalid Parameter */
+} DRESULT;
+
+
+/*---------------------------------------*/
+/* Prototypes for disk control functions */
+
+
+DSTATUS disk_initialize (BYTE pdrv);
+DSTATUS disk_status (BYTE pdrv);
+DRESULT disk_read (BYTE pdrv, BYTE* buff, LBA_t sector, UINT count);
+DRESULT disk_write (BYTE pdrv, const BYTE* buff, LBA_t sector, UINT count);
+DRESULT disk_ioctl (BYTE pdrv, BYTE cmd, void* buff);
+
+
+/* Disk Status Bits (DSTATUS) */
+
+#define STA_NOINIT             0x01    /* Drive not initialized */
+#define STA_NODISK             0x02    /* No medium in the drive */
+#define STA_PROTECT            0x04    /* Write protected */
+
+
+/* Command code for disk_ioctrl fucntion */
+
+/* Generic command (Used by FatFs) */
+#define CTRL_SYNC                      0       /* Complete pending write process (needed at FF_FS_READONLY == 0) */
+#define GET_SECTOR_COUNT       1       /* Get media size (needed at FF_USE_MKFS == 1) */
+#define GET_SECTOR_SIZE                2       /* Get sector size (needed at FF_MAX_SS != FF_MIN_SS) */
+#define GET_BLOCK_SIZE         3       /* Get erase block size (needed at FF_USE_MKFS == 1) */
+#define CTRL_TRIM                      4       /* Inform device that the data on the block of sectors is no longer used (needed at FF_USE_TRIM == 1) */
+
+/* Generic command (Not used by FatFs) */
+#define CTRL_POWER                     5       /* Get/Set power status */
+#define CTRL_LOCK                      6       /* Lock/Unlock media removal */
+#define CTRL_EJECT                     7       /* Eject media */
+#define CTRL_FORMAT                    8       /* Create physical format on the media */
+
+/* MMC/SDC specific ioctl command */
+#define MMC_GET_TYPE           10      /* Get card type */
+#define MMC_GET_CSD                    11      /* Get CSD */
+#define MMC_GET_CID                    12      /* Get CID */
+#define MMC_GET_OCR                    13      /* Get OCR */
+#define MMC_GET_SDSTAT         14      /* Get SD status */
+#define ISDIO_READ                     55      /* Read data form SD iSDIO register */
+#define ISDIO_WRITE                    56      /* Write data to SD iSDIO register */
+#define ISDIO_MRITE                    57      /* Masked write data to SD iSDIO register */
+
+/* ATA/CF specific ioctl command */
+#define ATA_GET_REV                    20      /* Get F/W revision */
+#define ATA_GET_MODEL          21      /* Get model name */
+#define ATA_GET_SN                     22      /* Get serial number */
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/ff14/source/ff.c b/Ventoy2Disk/Ventoy2Disk/ff14/source/ff.c
new file mode 100644 (file)
index 0000000..22cbac5
--- /dev/null
@@ -0,0 +1,6848 @@
+/*----------------------------------------------------------------------------/
+/  FatFs - Generic FAT Filesystem Module  R0.14                               /
+/-----------------------------------------------------------------------------/
+/
+/ Copyright (C) 2019, ChaN, all right reserved.
+/
+/ FatFs module is an open source software. Redistribution and use of FatFs in
+/ source and binary forms, with or without modification, are permitted provided
+/ that the following condition is met:
+/
+/ 1. Redistributions of source code must retain the above copyright notice,
+/    this condition and the following disclaimer.
+/
+/ This software is provided by the copyright holder and contributors "AS IS"
+/ and any warranties related to this software are DISCLAIMED.
+/ The copyright owner or contributors be NOT LIABLE for any damages caused
+/ by use of this software.
+/
+/----------------------------------------------------------------------------*/
+
+
+#include "ff.h"                        /* Declarations of FatFs API */
+#include "diskio.h"            /* Declarations of device I/O functions */
+
+
+/*--------------------------------------------------------------------------
+
+   Module Private Definitions
+
+---------------------------------------------------------------------------*/
+
+#if FF_DEFINED != 86606        /* Revision ID */
+#error Wrong include file (ff.h).
+#endif
+
+
+/* Limits and boundaries */
+#define MAX_DIR                0x200000                /* Max size of FAT directory */
+#define MAX_DIR_EX     0x10000000              /* Max size of exFAT directory */
+#define MAX_FAT12      0xFF5                   /* Max FAT12 clusters (differs from specs, but right for real DOS/Windows behavior) */
+#define MAX_FAT16      0xFFF5                  /* Max FAT16 clusters (differs from specs, but right for real DOS/Windows behavior) */
+#define MAX_FAT32      0x0FFFFFF5              /* Max FAT32 clusters (not specified, practical limit) */
+#define MAX_EXFAT      0x7FFFFFFD              /* Max exFAT clusters (differs from specs, implementation limit) */
+
+
+/* Character code support macros */
+#define IsUpper(c)             ((c) >= 'A' && (c) <= 'Z')
+#define IsLower(c)             ((c) >= 'a' && (c) <= 'z')
+#define IsDigit(c)             ((c) >= '0' && (c) <= '9')
+#define IsSurrogate(c) ((c) >= 0xD800 && (c) <= 0xDFFF)
+#define IsSurrogateH(c)        ((c) >= 0xD800 && (c) <= 0xDBFF)
+#define IsSurrogateL(c)        ((c) >= 0xDC00 && (c) <= 0xDFFF)
+
+
+/* Additional file access control and file status flags for internal use */
+#define FA_SEEKEND     0x20    /* Seek to end of the file on file open */
+#define FA_MODIFIED    0x40    /* File has been modified */
+#define FA_DIRTY       0x80    /* FIL.buf[] needs to be written-back */
+
+
+/* Additional file attribute bits for internal use */
+#define AM_VOL         0x08    /* Volume label */
+#define AM_LFN         0x0F    /* LFN entry */
+#define AM_MASK                0x3F    /* Mask of defined bits */
+
+
+/* Name status flags in fn[11] */
+#define NSFLAG         11              /* Index of the name status byte */
+#define NS_LOSS                0x01    /* Out of 8.3 format */
+#define NS_LFN         0x02    /* Force to create LFN entry */
+#define NS_LAST                0x04    /* Last segment */
+#define NS_BODY                0x08    /* Lower case flag (body) */
+#define NS_EXT         0x10    /* Lower case flag (ext) */
+#define NS_DOT         0x20    /* Dot entry */
+#define NS_NOLFN       0x40    /* Do not find LFN */
+#define NS_NONAME      0x80    /* Not followed */
+
+
+/* exFAT directory entry types */
+#define        ET_BITMAP       0x81    /* Allocation bitmap */
+#define        ET_UPCASE       0x82    /* Up-case table */
+#define        ET_VLABEL       0x83    /* Volume label */
+#define        ET_FILEDIR      0x85    /* File and directory */
+#define        ET_STREAM       0xC0    /* Stream extension */
+#define        ET_FILENAME     0xC1    /* Name extension */
+
+
+/* FatFs refers the FAT structure as simple byte array instead of structure member
+/ because the C structure is not binary compatible between different platforms */
+
+#define BS_JmpBoot                     0               /* x86 jump instruction (3-byte) */
+#define BS_OEMName                     3               /* OEM name (8-byte) */
+#define BPB_BytsPerSec         11              /* Sector size [byte] (WORD) */
+#define BPB_SecPerClus         13              /* Cluster size [sector] (BYTE) */
+#define BPB_RsvdSecCnt         14              /* Size of reserved area [sector] (WORD) */
+#define BPB_NumFATs                    16              /* Number of FATs (BYTE) */
+#define BPB_RootEntCnt         17              /* Size of root directory area for FAT [entry] (WORD) */
+#define BPB_TotSec16           19              /* Volume size (16-bit) [sector] (WORD) */
+#define BPB_Media                      21              /* Media descriptor byte (BYTE) */
+#define BPB_FATSz16                    22              /* FAT size (16-bit) [sector] (WORD) */
+#define BPB_SecPerTrk          24              /* Number of sectors per track for int13h [sector] (WORD) */
+#define BPB_NumHeads           26              /* Number of heads for int13h (WORD) */
+#define BPB_HiddSec                    28              /* Volume offset from top of the drive (DWORD) */
+#define BPB_TotSec32           32              /* Volume size (32-bit) [sector] (DWORD) */
+#define BS_DrvNum                      36              /* Physical drive number for int13h (BYTE) */
+#define BS_NTres                       37              /* WindowsNT error flag (BYTE) */
+#define BS_BootSig                     38              /* Extended boot signature (BYTE) */
+#define BS_VolID                       39              /* Volume serial number (DWORD) */
+#define BS_VolLab                      43              /* Volume label string (8-byte) */
+#define BS_FilSysType          54              /* Filesystem type string (8-byte) */
+#define BS_BootCode                    62              /* Boot code (448-byte) */
+#define BS_55AA                                510             /* Signature word (WORD) */
+
+#define BPB_FATSz32                    36              /* FAT32: FAT size [sector] (DWORD) */
+#define BPB_ExtFlags32         40              /* FAT32: Extended flags (WORD) */
+#define BPB_FSVer32                    42              /* FAT32: Filesystem version (WORD) */
+#define BPB_RootClus32         44              /* FAT32: Root directory cluster (DWORD) */
+#define BPB_FSInfo32           48              /* FAT32: Offset of FSINFO sector (WORD) */
+#define BPB_BkBootSec32                50              /* FAT32: Offset of backup boot sector (WORD) */
+#define BS_DrvNum32                    64              /* FAT32: Physical drive number for int13h (BYTE) */
+#define BS_NTres32                     65              /* FAT32: Error flag (BYTE) */
+#define BS_BootSig32           66              /* FAT32: Extended boot signature (BYTE) */
+#define BS_VolID32                     67              /* FAT32: Volume serial number (DWORD) */
+#define BS_VolLab32                    71              /* FAT32: Volume label string (8-byte) */
+#define BS_FilSysType32                82              /* FAT32: Filesystem type string (8-byte) */
+#define BS_BootCode32          90              /* FAT32: Boot code (420-byte) */
+
+#define BPB_ZeroedEx           11              /* exFAT: MBZ field (53-byte) */
+#define BPB_VolOfsEx           64              /* exFAT: Volume offset from top of the drive [sector] (QWORD) */
+#define BPB_TotSecEx           72              /* exFAT: Volume size [sector] (QWORD) */
+#define BPB_FatOfsEx           80              /* exFAT: FAT offset from top of the volume [sector] (DWORD) */
+#define BPB_FatSzEx                    84              /* exFAT: FAT size [sector] (DWORD) */
+#define BPB_DataOfsEx          88              /* exFAT: Data offset from top of the volume [sector] (DWORD) */
+#define BPB_NumClusEx          92              /* exFAT: Number of clusters (DWORD) */
+#define BPB_RootClusEx         96              /* exFAT: Root directory start cluster (DWORD) */
+#define BPB_VolIDEx                    100             /* exFAT: Volume serial number (DWORD) */
+#define BPB_FSVerEx                    104             /* exFAT: Filesystem version (WORD) */
+#define BPB_VolFlagEx          106             /* exFAT: Volume flags (WORD) */
+#define BPB_BytsPerSecEx       108             /* exFAT: Log2 of sector size in unit of byte (BYTE) */
+#define BPB_SecPerClusEx       109             /* exFAT: Log2 of cluster size in unit of sector (BYTE) */
+#define BPB_NumFATsEx          110             /* exFAT: Number of FATs (BYTE) */
+#define BPB_DrvNumEx           111             /* exFAT: Physical drive number for int13h (BYTE) */
+#define BPB_PercInUseEx                112             /* exFAT: Percent in use (BYTE) */
+#define BPB_RsvdEx                     113             /* exFAT: Reserved (7-byte) */
+#define BS_BootCodeEx          120             /* exFAT: Boot code (390-byte) */
+
+#define DIR_Name                       0               /* Short file name (11-byte) */
+#define DIR_Attr                       11              /* Attribute (BYTE) */
+#define DIR_NTres                      12              /* Lower case flag (BYTE) */
+#define DIR_CrtTime10          13              /* Created time sub-second (BYTE) */
+#define DIR_CrtTime                    14              /* Created time (DWORD) */
+#define DIR_LstAccDate         18              /* Last accessed date (WORD) */
+#define DIR_FstClusHI          20              /* Higher 16-bit of first cluster (WORD) */
+#define DIR_ModTime                    22              /* Modified time (DWORD) */
+#define DIR_FstClusLO          26              /* Lower 16-bit of first cluster (WORD) */
+#define DIR_FileSize           28              /* File size (DWORD) */
+#define LDIR_Ord                       0               /* LFN: LFN order and LLE flag (BYTE) */
+#define LDIR_Attr                      11              /* LFN: LFN attribute (BYTE) */
+#define LDIR_Type                      12              /* LFN: Entry type (BYTE) */
+#define LDIR_Chksum                    13              /* LFN: Checksum of the SFN (BYTE) */
+#define LDIR_FstClusLO         26              /* LFN: MBZ field (WORD) */
+#define XDIR_Type                      0               /* exFAT: Type of exFAT directory entry (BYTE) */
+#define XDIR_NumLabel          1               /* exFAT: Number of volume label characters (BYTE) */
+#define XDIR_Label                     2               /* exFAT: Volume label (11-WORD) */
+#define XDIR_CaseSum           4               /* exFAT: Sum of case conversion table (DWORD) */
+#define XDIR_NumSec                    1               /* exFAT: Number of secondary entries (BYTE) */
+#define XDIR_SetSum                    2               /* exFAT: Sum of the set of directory entries (WORD) */
+#define XDIR_Attr                      4               /* exFAT: File attribute (WORD) */
+#define XDIR_CrtTime           8               /* exFAT: Created time (DWORD) */
+#define XDIR_ModTime           12              /* exFAT: Modified time (DWORD) */
+#define XDIR_AccTime           16              /* exFAT: Last accessed time (DWORD) */
+#define XDIR_CrtTime10         20              /* exFAT: Created time subsecond (BYTE) */
+#define XDIR_ModTime10         21              /* exFAT: Modified time subsecond (BYTE) */
+#define XDIR_CrtTZ                     22              /* exFAT: Created timezone (BYTE) */
+#define XDIR_ModTZ                     23              /* exFAT: Modified timezone (BYTE) */
+#define XDIR_AccTZ                     24              /* exFAT: Last accessed timezone (BYTE) */
+#define XDIR_GenFlags          33              /* exFAT: General secondary flags (BYTE) */
+#define XDIR_NumName           35              /* exFAT: Number of file name characters (BYTE) */
+#define XDIR_NameHash          36              /* exFAT: Hash of file name (WORD) */
+#define XDIR_ValidFileSize     40              /* exFAT: Valid file size (QWORD) */
+#define XDIR_FstClus           52              /* exFAT: First cluster of the file data (DWORD) */
+#define XDIR_FileSize          56              /* exFAT: File/Directory size (QWORD) */
+
+#define SZDIRE                         32              /* Size of a directory entry */
+#define DDEM                           0xE5    /* Deleted directory entry mark set to DIR_Name[0] */
+#define RDDEM                          0x05    /* Replacement of the character collides with DDEM */
+#define LLEF                           0x40    /* Last long entry flag in LDIR_Ord */
+
+#define FSI_LeadSig                    0               /* FAT32 FSI: Leading signature (DWORD) */
+#define FSI_StrucSig           484             /* FAT32 FSI: Structure signature (DWORD) */
+#define FSI_Free_Count         488             /* FAT32 FSI: Number of free clusters (DWORD) */
+#define FSI_Nxt_Free           492             /* FAT32 FSI: Last allocated cluster (DWORD) */
+
+#define MBR_Table                      446             /* MBR: Offset of partition table in the MBR */
+#define SZ_PTE                         16              /* MBR: Size of a partition table entry */
+#define PTE_Boot                       0               /* MBR PTE: Boot indicator */
+#define PTE_StHead                     1               /* MBR PTE: Start head */
+#define PTE_StSec                      2               /* MBR PTE: Start sector */
+#define PTE_StCyl                      3               /* MBR PTE: Start cylinder */
+#define PTE_System                     4               /* MBR PTE: System ID */
+#define PTE_EdHead                     5               /* MBR PTE: End head */
+#define PTE_EdSec                      6               /* MBR PTE: End sector */
+#define PTE_EdCyl                      7               /* MBR PTE: End cylinder */
+#define PTE_StLba                      8               /* MBR PTE: Start in LBA */
+#define PTE_SizLba                     12              /* MBR PTE: Size in LBA */
+
+#define GPTH_Sign                      0               /* GPT: Header signature (8-byte) */
+#define GPTH_Rev                       8               /* GPT: Revision (DWORD) */
+#define GPTH_Size                      12              /* GPT: Header size (DWORD) */
+#define GPTH_Bcc                       16              /* GPT: Header BCC (DWORD) */
+#define GPTH_CurLba                    24              /* GPT: Main header LBA (QWORD) */
+#define GPTH_BakLba                    32              /* GPT: Backup header LBA (QWORD) */
+#define GPTH_FstLba                    40              /* GPT: First LBA for partitions (QWORD) */
+#define GPTH_LstLba                    48              /* GPT: Last LBA for partitions (QWORD) */
+#define GPTH_DskGuid           56              /* GPT: Disk GUID (16-byte) */
+#define GPTH_PtOfs                     72              /* GPT: Partation table LBA (QWORD) */
+#define GPTH_PtNum                     80              /* GPT: Number of table entries (DWORD) */
+#define GPTH_PteSize           84              /* GPT: Size of table entry (DWORD) */
+#define GPTH_PtBcc                     88              /* GPT: Partation table BCC (DWORD) */
+#define SZ_GPTE                                128             /* GPT: Size of partition table entry */
+#define GPTE_PtGuid                    0               /* GPT PTE: Partition type GUID (16-byte) */
+#define GPTE_UpGuid                    16              /* GPT PTE: Partition unique GUID (16-byte) */
+#define GPTE_FstLba                    32              /* GPT PTE: First LBA (QWORD) */
+#define GPTE_LstLba                    40              /* GPT PTE: Last LBA inclusive (QWORD) */
+#define GPTE_Flags                     48              /* GPT PTE: Flags (QWORD) */
+#define GPTE_Name                      56              /* GPT PTE: Name */
+
+
+/* Post process on fatal error in the file operations */
+#define ABORT(fs, res)         { fp->err = (BYTE)(res); LEAVE_FF(fs, res); }
+
+
+/* Re-entrancy related */
+#if FF_FS_REENTRANT
+#if FF_USE_LFN == 1
+#error Static LFN work area cannot be used at thread-safe configuration
+#endif
+#define LEAVE_FF(fs, res)      { unlock_fs(fs, res); return res; }
+#else
+#define LEAVE_FF(fs, res)      return res
+#endif
+
+
+/* Definitions of logical drive - physical location conversion */
+#if FF_MULTI_PARTITION
+#define LD2PD(vol) VolToPart[vol].pd   /* Get physical drive number */
+#define LD2PT(vol) VolToPart[vol].pt   /* Get partition index */
+#else
+#define LD2PD(vol) (BYTE)(vol) /* Each logical drive is associated with the same physical drive number */
+#define LD2PT(vol) 0                   /* Find first valid partition or in SFD */
+#endif
+
+
+/* Definitions of sector size */
+#if (FF_MAX_SS < FF_MIN_SS) || (FF_MAX_SS != 512 && FF_MAX_SS != 1024 && FF_MAX_SS != 2048 && FF_MAX_SS != 4096) || (FF_MIN_SS != 512 && FF_MIN_SS != 1024 && FF_MIN_SS != 2048 && FF_MIN_SS != 4096)
+#error Wrong sector size configuration
+#endif
+#if FF_MAX_SS == FF_MIN_SS
+#define SS(fs) ((UINT)FF_MAX_SS)       /* Fixed sector size */
+#else
+#define SS(fs) ((fs)->ssize)   /* Variable sector size */
+#endif
+
+
+/* Timestamp */
+#if FF_FS_NORTC == 1
+#if FF_NORTC_YEAR < 1980 || FF_NORTC_YEAR > 2107 || FF_NORTC_MON < 1 || FF_NORTC_MON > 12 || FF_NORTC_MDAY < 1 || FF_NORTC_MDAY > 31
+#error Invalid FF_FS_NORTC settings
+#endif
+#define GET_FATTIME()  ((DWORD)(FF_NORTC_YEAR - 1980) << 25 | (DWORD)FF_NORTC_MON << 21 | (DWORD)FF_NORTC_MDAY << 16)
+#else
+#define GET_FATTIME()  get_fattime()
+#endif
+
+
+/* File lock controls */
+#if FF_FS_LOCK != 0
+#if FF_FS_READONLY
+#error FF_FS_LOCK must be 0 at read-only configuration
+#endif
+typedef struct {
+       FATFS *fs;              /* Object ID 1, volume (NULL:blank entry) */
+       DWORD clu;              /* Object ID 2, containing directory (0:root) */
+       DWORD ofs;              /* Object ID 3, offset in the directory */
+       WORD ctr;               /* Object open counter, 0:none, 0x01..0xFF:read mode open count, 0x100:write mode */
+} FILESEM;
+#endif
+
+
+/* SBCS up-case tables (\x80-\xFF) */
+#define TBL_CT437  {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
+                                       0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                                       0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+                                       0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                                       0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+                                       0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                                       0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+                                       0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT720  {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+                                       0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                                       0xA0,0xA1,0xA2,0xA3,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+                                       0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                                       0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+                                       0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                                       0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+                                       0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT737  {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+                                       0x90,0x92,0x92,0x93,0x94,0x95,0x96,0x97,0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87, \
+                                       0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F,0x90,0x91,0xAA,0x92,0x93,0x94,0x95,0x96, \
+                                       0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                                       0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+                                       0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                                       0x97,0xEA,0xEB,0xEC,0xE4,0xED,0xEE,0xEF,0xF5,0xF0,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+                                       0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT771  {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+                                       0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                                       0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+                                       0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                                       0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+                                       0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDC,0xDE,0xDE, \
+                                       0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                                       0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFE,0xFF}
+#define TBL_CT775  {0x80,0x9A,0x91,0xA0,0x8E,0x95,0x8F,0x80,0xAD,0xED,0x8A,0x8A,0xA1,0x8D,0x8E,0x8F, \
+                                       0x90,0x92,0x92,0xE2,0x99,0x95,0x96,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+                                       0xA0,0xA1,0xE0,0xA3,0xA3,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+                                       0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                                       0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+                                       0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xA5,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                                       0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE3,0xE8,0xE8,0xEA,0xEA,0xEE,0xED,0xEE,0xEF, \
+                                       0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT850  {0x43,0x55,0x45,0x41,0x41,0x41,0x41,0x43,0x45,0x45,0x45,0x49,0x49,0x49,0x41,0x41, \
+                                       0x45,0x92,0x92,0x4F,0x4F,0x4F,0x55,0x55,0x59,0x4F,0x55,0x4F,0x9C,0x4F,0x9E,0x9F, \
+                                       0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+                                       0xB0,0xB1,0xB2,0xB3,0xB4,0x41,0x41,0x41,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                                       0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0x41,0x41,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+                                       0xD1,0xD1,0x45,0x45,0x45,0x49,0x49,0x49,0x49,0xD9,0xDA,0xDB,0xDC,0xDD,0x49,0xDF, \
+                                       0x4F,0xE1,0x4F,0x4F,0x4F,0x4F,0xE6,0xE8,0xE8,0x55,0x55,0x55,0x59,0x59,0xEE,0xEF, \
+                                       0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT852  {0x80,0x9A,0x90,0xB6,0x8E,0xDE,0x8F,0x80,0x9D,0xD3,0x8A,0x8A,0xD7,0x8D,0x8E,0x8F, \
+                                       0x90,0x91,0x91,0xE2,0x99,0x95,0x95,0x97,0x97,0x99,0x9A,0x9B,0x9B,0x9D,0x9E,0xAC, \
+                                       0xB5,0xD6,0xE0,0xE9,0xA4,0xA4,0xA6,0xA6,0xA8,0xA8,0xAA,0x8D,0xAC,0xB8,0xAE,0xAF, \
+                                       0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBD,0xBF, \
+                                       0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC6,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+                                       0xD1,0xD1,0xD2,0xD3,0xD2,0xD5,0xD6,0xD7,0xB7,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                                       0xE0,0xE1,0xE2,0xE3,0xE3,0xD5,0xE6,0xE6,0xE8,0xE9,0xE8,0xEB,0xED,0xED,0xDD,0xEF, \
+                                       0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xEB,0xFC,0xFC,0xFE,0xFF}
+#define TBL_CT855  {0x81,0x81,0x83,0x83,0x85,0x85,0x87,0x87,0x89,0x89,0x8B,0x8B,0x8D,0x8D,0x8F,0x8F, \
+                                       0x91,0x91,0x93,0x93,0x95,0x95,0x97,0x97,0x99,0x99,0x9B,0x9B,0x9D,0x9D,0x9F,0x9F, \
+                                       0xA1,0xA1,0xA3,0xA3,0xA5,0xA5,0xA7,0xA7,0xA9,0xA9,0xAB,0xAB,0xAD,0xAD,0xAE,0xAF, \
+                                       0xB0,0xB1,0xB2,0xB3,0xB4,0xB6,0xB6,0xB8,0xB8,0xB9,0xBA,0xBB,0xBC,0xBE,0xBE,0xBF, \
+                                       0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+                                       0xD1,0xD1,0xD3,0xD3,0xD5,0xD5,0xD7,0xD7,0xDD,0xD9,0xDA,0xDB,0xDC,0xDD,0xE0,0xDF, \
+                                       0xE0,0xE2,0xE2,0xE4,0xE4,0xE6,0xE6,0xE8,0xE8,0xEA,0xEA,0xEC,0xEC,0xEE,0xEE,0xEF, \
+                                       0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF8,0xFA,0xFA,0xFC,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT857  {0x80,0x9A,0x90,0xB6,0x8E,0xB7,0x8F,0x80,0xD2,0xD3,0xD4,0xD8,0xD7,0x49,0x8E,0x8F, \
+                                       0x90,0x92,0x92,0xE2,0x99,0xE3,0xEA,0xEB,0x98,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9E, \
+                                       0xB5,0xD6,0xE0,0xE9,0xA5,0xA5,0xA6,0xA6,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+                                       0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                                       0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC7,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+                                       0xD0,0xD1,0xD2,0xD3,0xD4,0x49,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                                       0xE0,0xE1,0xE2,0xE3,0xE5,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xDE,0xED,0xEE,0xEF, \
+                                       0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT860  {0x80,0x9A,0x90,0x8F,0x8E,0x91,0x86,0x80,0x89,0x89,0x92,0x8B,0x8C,0x98,0x8E,0x8F, \
+                                       0x90,0x91,0x92,0x8C,0x99,0xA9,0x96,0x9D,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                                       0x86,0x8B,0x9F,0x96,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+                                       0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                                       0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+                                       0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                                       0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+                                       0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT861  {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x8B,0x8B,0x8D,0x8E,0x8F, \
+                                       0x90,0x92,0x92,0x4F,0x99,0x8D,0x55,0x97,0x97,0x99,0x9A,0x9D,0x9C,0x9D,0x9E,0x9F, \
+                                       0xA4,0xA5,0xA6,0xA7,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+                                       0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                                       0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+                                       0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                                       0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+                                       0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT862  {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+                                       0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                                       0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+                                       0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                                       0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+                                       0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                                       0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+                                       0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT863  {0x43,0x55,0x45,0x41,0x41,0x41,0x86,0x43,0x45,0x45,0x45,0x49,0x49,0x8D,0x41,0x8F, \
+                                       0x45,0x45,0x45,0x4F,0x45,0x49,0x55,0x55,0x98,0x4F,0x55,0x9B,0x9C,0x55,0x55,0x9F, \
+                                       0xA0,0xA1,0x4F,0x55,0xA4,0xA5,0xA6,0xA7,0x49,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+                                       0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                                       0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+                                       0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                                       0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+                                       0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT864  {0x80,0x9A,0x45,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
+                                       0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                                       0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+                                       0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                                       0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+                                       0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                                       0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+                                       0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT865  {0x80,0x9A,0x90,0x41,0x8E,0x41,0x8F,0x80,0x45,0x45,0x45,0x49,0x49,0x49,0x8E,0x8F, \
+                                       0x90,0x92,0x92,0x4F,0x99,0x4F,0x55,0x55,0x59,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                                       0x41,0x49,0x4F,0x55,0xA5,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+                                       0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                                       0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+                                       0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                                       0xE0,0xE1,0xE2,0xE3,0xE4,0xE5,0xE6,0xE7,0xE8,0xE9,0xEA,0xEB,0xEC,0xED,0xEE,0xEF, \
+                                       0xF0,0xF1,0xF2,0xF3,0xF4,0xF5,0xF6,0xF7,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT866  {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+                                       0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                                       0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+                                       0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                                       0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+                                       0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xD6,0xD7,0xD8,0xD9,0xDA,0xDB,0xDC,0xDD,0xDE,0xDF, \
+                                       0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x9B,0x9C,0x9D,0x9E,0x9F, \
+                                       0xF0,0xF0,0xF2,0xF2,0xF4,0xF4,0xF6,0xF6,0xF8,0xF9,0xFA,0xFB,0xFC,0xFD,0xFE,0xFF}
+#define TBL_CT869  {0x80,0x81,0x82,0x83,0x84,0x85,0x86,0x87,0x88,0x89,0x8A,0x8B,0x8C,0x8D,0x8E,0x8F, \
+                                       0x90,0x91,0x92,0x93,0x94,0x95,0x96,0x97,0x98,0x99,0x9A,0x86,0x9C,0x8D,0x8F,0x90, \
+                                       0x91,0x90,0x92,0x95,0xA4,0xA5,0xA6,0xA7,0xA8,0xA9,0xAA,0xAB,0xAC,0xAD,0xAE,0xAF, \
+                                       0xB0,0xB1,0xB2,0xB3,0xB4,0xB5,0xB6,0xB7,0xB8,0xB9,0xBA,0xBB,0xBC,0xBD,0xBE,0xBF, \
+                                       0xC0,0xC1,0xC2,0xC3,0xC4,0xC5,0xC6,0xC7,0xC8,0xC9,0xCA,0xCB,0xCC,0xCD,0xCE,0xCF, \
+                                       0xD0,0xD1,0xD2,0xD3,0xD4,0xD5,0xA4,0xA5,0xA6,0xD9,0xDA,0xDB,0xDC,0xA7,0xA8,0xDF, \
+                                       0xA9,0xAA,0xAC,0xAD,0xB5,0xB6,0xB7,0xB8,0xBD,0xBE,0xC6,0xC7,0xCF,0xCF,0xD0,0xEF, \
+                                       0xF0,0xF1,0xD1,0xD2,0xD3,0xF5,0xD4,0xF7,0xF8,0xF9,0xD5,0x96,0x95,0x98,0xFE,0xFF}
+
+
+/* DBCS code range |----- 1st byte -----|  |----------- 2nd byte -----------| */
+/*                  <------>    <------>    <------>    <------>    <------>  */
+#define TBL_DC932 {0x81, 0x9F, 0xE0, 0xFC, 0x40, 0x7E, 0x80, 0xFC, 0x00, 0x00}
+#define TBL_DC936 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0x80, 0xFE, 0x00, 0x00}
+#define TBL_DC949 {0x81, 0xFE, 0x00, 0x00, 0x41, 0x5A, 0x61, 0x7A, 0x81, 0xFE}
+#define TBL_DC950 {0x81, 0xFE, 0x00, 0x00, 0x40, 0x7E, 0xA1, 0xFE, 0x00, 0x00}
+
+
+/* Macros for table definitions */
+#define MERGE_2STR(a, b) a ## b
+#define MKCVTBL(hd, cp) MERGE_2STR(hd, cp)
+
+
+
+
+/*--------------------------------------------------------------------------
+
+   Module Private Work Area
+
+---------------------------------------------------------------------------*/
+/* Remark: Variables defined here without initial value shall be guaranteed
+/  zero/null at start-up. If not, the linker option or start-up routine is
+/  not compliance with C standard. */
+
+/*--------------------------------*/
+/* File/Volume controls           */
+/*--------------------------------*/
+
+#if FF_VOLUMES < 1 || FF_VOLUMES > 10
+#error Wrong FF_VOLUMES setting
+#endif
+static FATFS* FatFs[FF_VOLUMES];       /* Pointer to the filesystem objects (logical drives) */
+static WORD Fsid;                                      /* Filesystem mount ID */
+
+#if FF_FS_RPATH != 0
+static BYTE CurrVol;                           /* Current drive */
+#endif
+
+#if FF_FS_LOCK != 0
+static FILESEM Files[FF_FS_LOCK];      /* Open object lock semaphores */
+#endif
+
+#if FF_STR_VOLUME_ID
+#ifdef FF_VOLUME_STRS
+static const char* const VolumeStr[FF_VOLUMES] = {FF_VOLUME_STRS};     /* Pre-defined volume ID */
+#endif
+#endif
+
+#if FF_LBA64
+#if FF_MIN_GPT > 0x100000000
+#error Wrong FF_MIN_GPT setting
+#endif
+static const BYTE GUID_MS_Basic[16] = {0xA2,0xA0,0xD0,0xEB,0xE5,0xB9,0x33,0x44,0x87,0xC0,0x68,0xB6,0xB7,0x26,0x99,0xC7};
+#endif
+
+
+
+/*--------------------------------*/
+/* LFN/Directory working buffer   */
+/*--------------------------------*/
+
+#if FF_USE_LFN == 0            /* Non-LFN configuration */
+#if FF_FS_EXFAT
+#error LFN must be enabled when enable exFAT
+#endif
+#define DEF_NAMBUF
+#define INIT_NAMBUF(fs)
+#define FREE_NAMBUF()
+#define LEAVE_MKFS(res)        return res
+
+#else                                  /* LFN configurations */
+#if FF_MAX_LFN < 12 || FF_MAX_LFN > 255
+#error Wrong setting of FF_MAX_LFN
+#endif
+#if FF_LFN_BUF < FF_SFN_BUF || FF_SFN_BUF < 12
+#error Wrong setting of FF_LFN_BUF or FF_SFN_BUF
+#endif
+#if FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3
+#error Wrong setting of FF_LFN_UNICODE
+#endif
+static const BYTE LfnOfs[] = {1,3,5,7,9,14,16,18,20,22,24,28,30};      /* FAT: Offset of LFN characters in the directory entry */
+#define MAXDIRB(nc)    ((nc + 44U) / 15 * SZDIRE)      /* exFAT: Size of directory entry block scratchpad buffer needed for the name length */
+
+#if FF_USE_LFN == 1            /* LFN enabled with static working buffer */
+#if FF_FS_EXFAT
+static BYTE    DirBuf[MAXDIRB(FF_MAX_LFN)];    /* Directory entry block scratchpad buffer */
+#endif
+static WCHAR LfnBuf[FF_MAX_LFN + 1];           /* LFN working buffer */
+#define DEF_NAMBUF
+#define INIT_NAMBUF(fs)
+#define FREE_NAMBUF()
+#define LEAVE_MKFS(res)        return res
+
+#elif FF_USE_LFN == 2  /* LFN enabled with dynamic working buffer on the stack */
+#if FF_FS_EXFAT
+#define DEF_NAMBUF             WCHAR lbuf[FF_MAX_LFN+1]; BYTE dbuf[MAXDIRB(FF_MAX_LFN)];       /* LFN working buffer and directory entry block scratchpad buffer */
+#define INIT_NAMBUF(fs)        { (fs)->lfnbuf = lbuf; (fs)->dirbuf = dbuf; }
+#define FREE_NAMBUF()
+#else
+#define DEF_NAMBUF             WCHAR lbuf[FF_MAX_LFN+1];       /* LFN working buffer */
+#define INIT_NAMBUF(fs)        { (fs)->lfnbuf = lbuf; }
+#define FREE_NAMBUF()
+#endif
+#define LEAVE_MKFS(res)        return res
+
+#elif FF_USE_LFN == 3  /* LFN enabled with dynamic working buffer on the heap */
+#if FF_FS_EXFAT
+#define DEF_NAMBUF             WCHAR *lfn;     /* Pointer to LFN working buffer and directory entry block scratchpad buffer */
+#define INIT_NAMBUF(fs)        { lfn = ff_memalloc((FF_MAX_LFN+1)*2 + MAXDIRB(FF_MAX_LFN)); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; (fs)->dirbuf = (BYTE*)(lfn+FF_MAX_LFN+1); }
+#define FREE_NAMBUF()  ff_memfree(lfn)
+#else
+#define DEF_NAMBUF             WCHAR *lfn;     /* Pointer to LFN working buffer */
+#define INIT_NAMBUF(fs)        { lfn = ff_memalloc((FF_MAX_LFN+1)*2); if (!lfn) LEAVE_FF(fs, FR_NOT_ENOUGH_CORE); (fs)->lfnbuf = lfn; }
+#define FREE_NAMBUF()  ff_memfree(lfn)
+#endif
+#define LEAVE_MKFS(res)        { if (!work) ff_memfree(buf); return res; }
+#define MAX_MALLOC     0x8000  /* Must be >=FF_MAX_SS */
+
+#else
+#error Wrong setting of FF_USE_LFN
+
+#endif /* FF_USE_LFN == 1 */
+#endif /* FF_USE_LFN == 0 */
+
+
+
+/*--------------------------------*/
+/* Code conversion tables         */
+/*--------------------------------*/
+
+#if FF_CODE_PAGE == 0          /* Run-time code page configuration */
+#define CODEPAGE CodePage
+static WORD CodePage;  /* Current code page */
+static const BYTE *ExCvt, *DbcTbl;     /* Pointer to current SBCS up-case table and DBCS code range table below */
+
+static const BYTE Ct437[] = TBL_CT437;
+static const BYTE Ct720[] = TBL_CT720;
+static const BYTE Ct737[] = TBL_CT737;
+static const BYTE Ct771[] = TBL_CT771;
+static const BYTE Ct775[] = TBL_CT775;
+static const BYTE Ct850[] = TBL_CT850;
+static const BYTE Ct852[] = TBL_CT852;
+static const BYTE Ct855[] = TBL_CT855;
+static const BYTE Ct857[] = TBL_CT857;
+static const BYTE Ct860[] = TBL_CT860;
+static const BYTE Ct861[] = TBL_CT861;
+static const BYTE Ct862[] = TBL_CT862;
+static const BYTE Ct863[] = TBL_CT863;
+static const BYTE Ct864[] = TBL_CT864;
+static const BYTE Ct865[] = TBL_CT865;
+static const BYTE Ct866[] = TBL_CT866;
+static const BYTE Ct869[] = TBL_CT869;
+static const BYTE Dc932[] = TBL_DC932;
+static const BYTE Dc936[] = TBL_DC936;
+static const BYTE Dc949[] = TBL_DC949;
+static const BYTE Dc950[] = TBL_DC950;
+
+#elif FF_CODE_PAGE < 900       /* Static code page configuration (SBCS) */
+#define CODEPAGE FF_CODE_PAGE
+static const BYTE ExCvt[] = MKCVTBL(TBL_CT, FF_CODE_PAGE);
+
+#else                                  /* Static code page configuration (DBCS) */
+#define CODEPAGE FF_CODE_PAGE
+static const BYTE DbcTbl[] = MKCVTBL(TBL_DC, FF_CODE_PAGE);
+
+#endif
+
+
+
+
+/*--------------------------------------------------------------------------
+
+   Module Private Functions
+
+---------------------------------------------------------------------------*/
+
+
+/*-----------------------------------------------------------------------*/
+/* Load/Store multi-byte word in the FAT structure                       */
+/*-----------------------------------------------------------------------*/
+
+static WORD ld_word (const BYTE* ptr)  /*       Load a 2-byte little-endian word */
+{
+       WORD rv;
+
+       rv = ptr[1];
+       rv = rv << 8 | ptr[0];
+       return rv;
+}
+
+static DWORD ld_dword (const BYTE* ptr)        /* Load a 4-byte little-endian word */
+{
+       DWORD rv;
+
+       rv = ptr[3];
+       rv = rv << 8 | ptr[2];
+       rv = rv << 8 | ptr[1];
+       rv = rv << 8 | ptr[0];
+       return rv;
+}
+
+#if FF_FS_EXFAT
+static QWORD ld_qword (const BYTE* ptr)        /* Load an 8-byte little-endian word */
+{
+       QWORD rv;
+
+       rv = ptr[7];
+       rv = rv << 8 | ptr[6];
+       rv = rv << 8 | ptr[5];
+       rv = rv << 8 | ptr[4];
+       rv = rv << 8 | ptr[3];
+       rv = rv << 8 | ptr[2];
+       rv = rv << 8 | ptr[1];
+       rv = rv << 8 | ptr[0];
+       return rv;
+}
+#endif
+
+#if !FF_FS_READONLY
+static void st_word (BYTE* ptr, WORD val)      /* Store a 2-byte word in little-endian */
+{
+       *ptr++ = (BYTE)val; val >>= 8;
+       *ptr++ = (BYTE)val;
+}
+
+static void st_dword (BYTE* ptr, DWORD val)    /* Store a 4-byte word in little-endian */
+{
+       *ptr++ = (BYTE)val; val >>= 8;
+       *ptr++ = (BYTE)val; val >>= 8;
+       *ptr++ = (BYTE)val; val >>= 8;
+       *ptr++ = (BYTE)val;
+}
+
+#if FF_FS_EXFAT
+static void st_qword (BYTE* ptr, QWORD val)    /* Store an 8-byte word in little-endian */
+{
+       *ptr++ = (BYTE)val; val >>= 8;
+       *ptr++ = (BYTE)val; val >>= 8;
+       *ptr++ = (BYTE)val; val >>= 8;
+       *ptr++ = (BYTE)val; val >>= 8;
+       *ptr++ = (BYTE)val; val >>= 8;
+       *ptr++ = (BYTE)val; val >>= 8;
+       *ptr++ = (BYTE)val; val >>= 8;
+       *ptr++ = (BYTE)val;
+}
+#endif
+#endif /* !FF_FS_READONLY */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* String functions                                                      */
+/*-----------------------------------------------------------------------*/
+
+/* Copy memory to memory */
+static void mem_cpy (void* dst, const void* src, UINT cnt)
+{
+       BYTE *d = (BYTE*)dst;
+       const BYTE *s = (const BYTE*)src;
+
+       if (cnt != 0) {
+               do {
+                       *d++ = *s++;
+               } while (--cnt);
+       }
+}
+
+
+/* Fill memory block */
+static void mem_set (void* dst, int val, UINT cnt)
+{
+       BYTE *d = (BYTE*)dst;
+
+       do {
+               *d++ = (BYTE)val;
+       } while (--cnt);
+}
+
+
+/* Compare memory block */
+static int mem_cmp (const void* dst, const void* src, UINT cnt)        /* ZR:same, NZ:different */
+{
+       const BYTE *d = (const BYTE *)dst, *s = (const BYTE *)src;
+       int r = 0;
+
+       do {
+               r = *d++ - *s++;
+       } while (--cnt && r == 0);
+
+       return r;
+}
+
+
+/* Check if chr is contained in the string */
+static int chk_chr (const char* str, int chr)  /* NZ:contained, ZR:not contained */
+{
+       while (*str && *str != chr) str++;
+       return *str;
+}
+
+
+/* Test if the byte is DBC 1st byte */
+static int dbc_1st (BYTE c)
+{
+#if FF_CODE_PAGE == 0          /* Variable code page */
+       if (DbcTbl && c >= DbcTbl[0]) {
+               if (c <= DbcTbl[1]) return 1;                                   /* 1st byte range 1 */
+               if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1; /* 1st byte range 2 */
+       }
+#elif FF_CODE_PAGE >= 900      /* DBCS fixed code page */
+       if (c >= DbcTbl[0]) {
+               if (c <= DbcTbl[1]) return 1;
+               if (c >= DbcTbl[2] && c <= DbcTbl[3]) return 1;
+       }
+#else                                          /* SBCS fixed code page */
+       if (c != 0) return 0;   /* Always false */
+#endif
+       return 0;
+}
+
+
+/* Test if the byte is DBC 2nd byte */
+static int dbc_2nd (BYTE c)
+{
+#if FF_CODE_PAGE == 0          /* Variable code page */
+       if (DbcTbl && c >= DbcTbl[4]) {
+               if (c <= DbcTbl[5]) return 1;                                   /* 2nd byte range 1 */
+               if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1; /* 2nd byte range 2 */
+               if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1; /* 2nd byte range 3 */
+       }
+#elif FF_CODE_PAGE >= 900      /* DBCS fixed code page */
+       if (c >= DbcTbl[4]) {
+               if (c <= DbcTbl[5]) return 1;
+               if (c >= DbcTbl[6] && c <= DbcTbl[7]) return 1;
+               if (c >= DbcTbl[8] && c <= DbcTbl[9]) return 1;
+       }
+#else                                          /* SBCS fixed code page */
+       if (c != 0) return 0;   /* Always false */
+#endif
+       return 0;
+}
+
+
+#if FF_USE_LFN
+
+/* Get a Unicode code point from the TCHAR string in defined API encodeing */
+static DWORD tchar2uni (       /* Returns a character in UTF-16 encoding (>=0x10000 on surrogate pair, 0xFFFFFFFF on decode error) */
+       const TCHAR** str               /* Pointer to pointer to TCHAR string in configured encoding */
+)
+{
+       DWORD uc;
+       const TCHAR *p = *str;
+
+#if FF_LFN_UNICODE == 1                /* UTF-16 input */
+       WCHAR wc;
+
+       uc = *p++;      /* Get a unit */
+       if (IsSurrogate(uc)) {  /* Surrogate? */
+               wc = *p++;              /* Get low surrogate */
+               if (!IsSurrogateH(uc) || !IsSurrogateL(wc)) return 0xFFFFFFFF;  /* Wrong surrogate? */
+               uc = uc << 16 | wc;
+       }
+
+#elif FF_LFN_UNICODE == 2      /* UTF-8 input */
+       BYTE b;
+       int nf;
+
+       uc = (BYTE)*p++;        /* Get an encoding unit */
+       if (uc & 0x80) {        /* Multiple byte code? */
+               if ((uc & 0xE0) == 0xC0) {      /* 2-byte sequence? */
+                       uc &= 0x1F; nf = 1;
+               } else {
+                       if ((uc & 0xF0) == 0xE0) {      /* 3-byte sequence? */
+                               uc &= 0x0F; nf = 2;
+                       } else {
+                               if ((uc & 0xF8) == 0xF0) {      /* 4-byte sequence? */
+                                       uc &= 0x07; nf = 3;
+                               } else {                                        /* Wrong sequence */
+                                       return 0xFFFFFFFF;
+                               }
+                       }
+               }
+               do {    /* Get trailing bytes */
+                       b = (BYTE)*p++;
+                       if ((b & 0xC0) != 0x80) return 0xFFFFFFFF;      /* Wrong sequence? */
+                       uc = uc << 6 | (b & 0x3F);
+               } while (--nf != 0);
+               if (uc < 0x80 || IsSurrogate(uc) || uc >= 0x110000) return 0xFFFFFFFF;  /* Wrong code? */
+               if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */
+       }
+
+#elif FF_LFN_UNICODE == 3      /* UTF-32 input */
+       uc = (TCHAR)*p++;       /* Get a unit */
+       if (uc >= 0x110000 || IsSurrogate(uc)) return 0xFFFFFFFF;       /* Wrong code? */
+       if (uc >= 0x010000) uc = 0xD800DC00 | ((uc - 0x10000) << 6 & 0x3FF0000) | (uc & 0x3FF); /* Make a surrogate pair if needed */
+
+#else          /* ANSI/OEM input */
+       BYTE b;
+       WCHAR wc;
+
+       wc = (BYTE)*p++;                        /* Get a byte */
+       if (dbc_1st((BYTE)wc)) {        /* Is it a DBC 1st byte? */
+               b = (BYTE)*p++;                 /* Get 2nd byte */
+               if (!dbc_2nd(b)) return 0xFFFFFFFF;     /* Invalid code? */
+               wc = (wc << 8) + b;             /* Make a DBC */
+       }
+       if (wc != 0) {
+               wc = ff_oem2uni(wc, CODEPAGE);  /* ANSI/OEM ==> Unicode */
+               if (wc == 0) return 0xFFFFFFFF; /* Invalid code? */
+       }
+       uc = wc;
+
+#endif
+       *str = p;       /* Next read pointer */
+       return uc;
+}
+
+
+/* Output a TCHAR string in defined API encoding */
+static BYTE put_utf (  /* Returns number of encoding units written (0:buffer overflow or wrong encoding) */
+       DWORD chr,      /* UTF-16 encoded character (Surrogate pair if >=0x10000) */
+       TCHAR* buf,     /* Output buffer */
+       UINT szb        /* Size of the buffer */
+)
+{
+#if FF_LFN_UNICODE == 1        /* UTF-16 output */
+       WCHAR hs, wc;
+
+       hs = (WCHAR)(chr >> 16);
+       wc = (WCHAR)chr;
+       if (hs == 0) {  /* Single encoding unit? */
+               if (szb < 1 || IsSurrogate(wc)) return 0;       /* Buffer overflow or wrong code? */
+               *buf = wc;
+               return 1;
+       }
+       if (szb < 2 || !IsSurrogateH(hs) || !IsSurrogateL(wc)) return 0;        /* Buffer overflow or wrong surrogate? */
+       *buf++ = hs;
+       *buf++ = wc;
+       return 2;
+
+#elif FF_LFN_UNICODE == 2      /* UTF-8 output */
+       DWORD hc;
+
+       if (chr < 0x80) {       /* Single byte code? */
+               if (szb < 1) return 0;  /* Buffer overflow? */
+               *buf = (TCHAR)chr;
+               return 1;
+       }
+       if (chr < 0x800) {      /* 2-byte sequence? */
+               if (szb < 2) return 0;  /* Buffer overflow? */
+               *buf++ = (TCHAR)(0xC0 | (chr >> 6 & 0x1F));
+               *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F));
+               return 2;
+       }
+       if (chr < 0x10000) {    /* 3-byte sequence? */
+               if (szb < 3 || IsSurrogate(chr)) return 0;      /* Buffer overflow or wrong code? */
+               *buf++ = (TCHAR)(0xE0 | (chr >> 12 & 0x0F));
+               *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F));
+               *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F));
+               return 3;
+       }
+       /* 4-byte sequence */
+       if (szb < 4) return 0;  /* Buffer overflow? */
+       hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6;    /* Get high 10 bits */
+       chr = (chr & 0xFFFF) - 0xDC00;                                  /* Get low 10 bits */
+       if (hc >= 0x100000 || chr >= 0x400) return 0;   /* Wrong surrogate? */
+       chr = (hc | chr) + 0x10000;
+       *buf++ = (TCHAR)(0xF0 | (chr >> 18 & 0x07));
+       *buf++ = (TCHAR)(0x80 | (chr >> 12 & 0x3F));
+       *buf++ = (TCHAR)(0x80 | (chr >> 6 & 0x3F));
+       *buf++ = (TCHAR)(0x80 | (chr >> 0 & 0x3F));
+       return 4;
+
+#elif FF_LFN_UNICODE == 3      /* UTF-32 output */
+       DWORD hc;
+
+       if (szb < 1) return 0;  /* Buffer overflow? */
+       if (chr >= 0x10000) {   /* Out of BMP? */
+               hc = ((chr & 0xFFFF0000) - 0xD8000000) >> 6;    /* Get high 10 bits */
+               chr = (chr & 0xFFFF) - 0xDC00;                                  /* Get low 10 bits */
+               if (hc >= 0x100000 || chr >= 0x400) return 0;   /* Wrong surrogate? */
+               chr = (hc | chr) + 0x10000;
+       }
+       *buf++ = (TCHAR)chr;
+       return 1;
+
+#else                                          /* ANSI/OEM output */
+       WCHAR wc;
+
+       wc = ff_uni2oem(chr, CODEPAGE);
+       if (wc >= 0x100) {      /* Is this a DBC? */
+               if (szb < 2) return 0;
+               *buf++ = (char)(wc >> 8);       /* Store DBC 1st byte */
+               *buf++ = (TCHAR)wc;                     /* Store DBC 2nd byte */
+               return 2;
+       }
+       if (wc == 0 || szb < 1) return 0;       /* Invalid char or buffer overflow? */
+       *buf++ = (TCHAR)wc;                                     /* Store the character */
+       return 1;
+#endif
+}
+#endif /* FF_USE_LFN */
+
+
+#if FF_FS_REENTRANT
+/*-----------------------------------------------------------------------*/
+/* Request/Release grant to access the volume                            */
+/*-----------------------------------------------------------------------*/
+static int lock_fs (           /* 1:Ok, 0:timeout */
+       FATFS* fs               /* Filesystem object */
+)
+{
+       return ff_req_grant(fs->sobj);
+}
+
+
+static void unlock_fs (
+       FATFS* fs,              /* Filesystem object */
+       FRESULT res             /* Result code to be returned */
+)
+{
+       if (fs && res != FR_NOT_ENABLED && res != FR_INVALID_DRIVE && res != FR_TIMEOUT) {
+               ff_rel_grant(fs->sobj);
+       }
+}
+
+#endif
+
+
+
+#if FF_FS_LOCK != 0
+/*-----------------------------------------------------------------------*/
+/* File lock control functions                                           */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT chk_lock (      /* Check if the file can be accessed */
+       DIR* dp,                /* Directory object pointing the file to be checked */
+       int acc                 /* Desired access type (0:Read mode open, 1:Write mode open, 2:Delete or rename) */
+)
+{
+       UINT i, be;
+
+       /* Search open object table for the object */
+       be = 0;
+       for (i = 0; i < FF_FS_LOCK; i++) {
+               if (Files[i].fs) {      /* Existing entry */
+                       if (Files[i].fs == dp->obj.fs &&                /* Check if the object matches with an open object */
+                               Files[i].clu == dp->obj.sclust &&
+                               Files[i].ofs == dp->dptr) break;
+               } else {                        /* Blank entry */
+                       be = 1;
+               }
+       }
+       if (i == FF_FS_LOCK) {  /* The object has not been opened */
+               return (!be && acc != 2) ? FR_TOO_MANY_OPEN_FILES : FR_OK;      /* Is there a blank entry for new object? */
+       }
+
+       /* The object was opened. Reject any open against writing file and all write mode open */
+       return (acc != 0 || Files[i].ctr == 0x100) ? FR_LOCKED : FR_OK;
+}
+
+
+static int enq_lock (void)     /* Check if an entry is available for a new object */
+{
+       UINT i;
+
+       for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ;
+       return (i == FF_FS_LOCK) ? 0 : 1;
+}
+
+
+static UINT inc_lock ( /* Increment object open counter and returns its index (0:Internal error) */
+       DIR* dp,        /* Directory object pointing the file to register or increment */
+       int acc         /* Desired access (0:Read, 1:Write, 2:Delete/Rename) */
+)
+{
+       UINT i;
+
+
+       for (i = 0; i < FF_FS_LOCK; i++) {      /* Find the object */
+               if (Files[i].fs == dp->obj.fs
+                && Files[i].clu == dp->obj.sclust
+                && Files[i].ofs == dp->dptr) break;
+       }
+
+       if (i == FF_FS_LOCK) {                          /* Not opened. Register it as new. */
+               for (i = 0; i < FF_FS_LOCK && Files[i].fs; i++) ;
+               if (i == FF_FS_LOCK) return 0;  /* No free entry to register (int err) */
+               Files[i].fs = dp->obj.fs;
+               Files[i].clu = dp->obj.sclust;
+               Files[i].ofs = dp->dptr;
+               Files[i].ctr = 0;
+       }
+
+       if (acc >= 1 && Files[i].ctr) return 0; /* Access violation (int err) */
+
+       Files[i].ctr = acc ? 0x100 : Files[i].ctr + 1;  /* Set semaphore value */
+
+       return i + 1;   /* Index number origin from 1 */
+}
+
+
+static FRESULT dec_lock (      /* Decrement object open counter */
+       UINT i                  /* Semaphore index (1..) */
+)
+{
+       WORD n;
+       FRESULT res;
+
+
+       if (--i < FF_FS_LOCK) { /* Index number origin from 0 */
+               n = Files[i].ctr;
+               if (n == 0x100) n = 0;          /* If write mode open, delete the entry */
+               if (n > 0) n--;                         /* Decrement read mode open count */
+               Files[i].ctr = n;
+               if (n == 0) Files[i].fs = 0;    /* Delete the entry if open count gets zero */
+               res = FR_OK;
+       } else {
+               res = FR_INT_ERR;                       /* Invalid index nunber */
+       }
+       return res;
+}
+
+
+static void clear_lock (       /* Clear lock entries of the volume */
+       FATFS *fs
+)
+{
+       UINT i;
+
+       for (i = 0; i < FF_FS_LOCK; i++) {
+               if (Files[i].fs == fs) Files[i].fs = 0;
+       }
+}
+
+#endif /* FF_FS_LOCK != 0 */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Move/Flush disk access window in the filesystem object                */
+/*-----------------------------------------------------------------------*/
+#if !FF_FS_READONLY
+static FRESULT sync_window (   /* Returns FR_OK or FR_DISK_ERR */
+       FATFS* fs                       /* Filesystem object */
+)
+{
+       FRESULT res = FR_OK;
+
+
+       if (fs->wflag) {        /* Is the disk access window dirty? */
+               if (disk_write(fs->pdrv, fs->win, fs->winsect, 1) == RES_OK) {  /* Write it back into the volume */
+                       fs->wflag = 0;  /* Clear window dirty flag */
+                       if (fs->winsect - fs->fatbase < fs->fsize) {    /* Is it in the 1st FAT? */
+                               if (fs->n_fats == 2) disk_write(fs->pdrv, fs->win, fs->winsect + fs->fsize, 1); /* Reflect it to 2nd FAT if needed */
+                       }
+               } else {
+                       res = FR_DISK_ERR;
+               }
+       }
+       return res;
+}
+#endif
+
+
+static FRESULT move_window (   /* Returns FR_OK or FR_DISK_ERR */
+       FATFS* fs,              /* Filesystem object */
+       LBA_t sect              /* Sector LBA to make appearance in the fs->win[] */
+)
+{
+       FRESULT res = FR_OK;
+
+
+       if (sect != fs->winsect) {      /* Window offset changed? */
+#if !FF_FS_READONLY
+               res = sync_window(fs);          /* Flush the window */
+#endif
+               if (res == FR_OK) {                     /* Fill sector window with new data */
+                       if (disk_read(fs->pdrv, fs->win, sect, 1) != RES_OK) {
+                               sect = (LBA_t)0 - 1;    /* Invalidate window if read data is not valid */
+                               res = FR_DISK_ERR;
+                       }
+                       fs->winsect = sect;
+               }
+       }
+       return res;
+}
+
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Synchronize filesystem and data on the storage                        */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT sync_fs (       /* Returns FR_OK or FR_DISK_ERR */
+       FATFS* fs               /* Filesystem object */
+)
+{
+       FRESULT res;
+
+
+       res = sync_window(fs);
+       if (res == FR_OK) {
+               if (fs->fs_type == FS_FAT32 && fs->fsi_flag == 1) {     /* FAT32: Update FSInfo sector if needed */
+                       /* Create FSInfo structure */
+                       mem_set(fs->win, 0, sizeof fs->win);
+                       st_word(fs->win + BS_55AA, 0xAA55);
+                       st_dword(fs->win + FSI_LeadSig, 0x41615252);
+                       st_dword(fs->win + FSI_StrucSig, 0x61417272);
+                       st_dword(fs->win + FSI_Free_Count, fs->free_clst);
+                       st_dword(fs->win + FSI_Nxt_Free, fs->last_clst);
+                       /* Write it into the FSInfo sector */
+                       fs->winsect = fs->volbase + 1;
+                       disk_write(fs->pdrv, fs->win, fs->winsect, 1);
+                       fs->fsi_flag = 0;
+               }
+               /* Make sure that no pending write process in the lower layer */
+               if (disk_ioctl(fs->pdrv, CTRL_SYNC, 0) != RES_OK) res = FR_DISK_ERR;
+       }
+
+       return res;
+}
+
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get physical sector number from cluster number                        */
+/*-----------------------------------------------------------------------*/
+
+static LBA_t clst2sect (       /* !=0:Sector number, 0:Failed (invalid cluster#) */
+       FATFS* fs,              /* Filesystem object */
+       DWORD clst              /* Cluster# to be converted */
+)
+{
+       clst -= 2;              /* Cluster number is origin from 2 */
+       if (clst >= fs->n_fatent - 2) return 0;         /* Is it invalid cluster number? */
+       return fs->database + (LBA_t)fs->csize * clst;  /* Start sector number of the cluster */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT access - Read value of a FAT entry                                */
+/*-----------------------------------------------------------------------*/
+
+static DWORD get_fat (         /* 0xFFFFFFFF:Disk error, 1:Internal error, 2..0x7FFFFFFF:Cluster status */
+       FFOBJID* obj,   /* Corresponding object */
+       DWORD clst              /* Cluster number to get the value */
+)
+{
+       UINT wc, bc;
+       DWORD val;
+       FATFS *fs = obj->fs;
+
+
+       if (clst < 2 || clst >= fs->n_fatent) { /* Check if in valid range */
+               val = 1;        /* Internal error */
+
+       } else {
+               val = 0xFFFFFFFF;       /* Default value falls on disk error */
+
+               switch (fs->fs_type) {
+               case FS_FAT12 :
+                       bc = (UINT)clst; bc += bc / 2;
+                       if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break;
+                       wc = fs->win[bc++ % SS(fs)];            /* Get 1st byte of the entry */
+                       if (move_window(fs, fs->fatbase + (bc / SS(fs))) != FR_OK) break;
+                       wc |= fs->win[bc % SS(fs)] << 8;        /* Merge 2nd byte of the entry */
+                       val = (clst & 1) ? (wc >> 4) : (wc & 0xFFF);    /* Adjust bit position */
+                       break;
+
+               case FS_FAT16 :
+                       if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 2))) != FR_OK) break;
+                       val = ld_word(fs->win + clst * 2 % SS(fs));             /* Simple WORD array */
+                       break;
+
+               case FS_FAT32 :
+                       if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break;
+                       val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x0FFFFFFF;       /* Simple DWORD array but mask out upper 4 bits */
+                       break;
+#if FF_FS_EXFAT
+               case FS_EXFAT :
+                       if ((obj->objsize != 0 && obj->sclust != 0) || obj->stat == 0) {        /* Object except root dir must have valid data length */
+                               DWORD cofs = clst - obj->sclust;        /* Offset from start cluster */
+                               DWORD clen = (DWORD)((LBA_t)((obj->objsize - 1) / SS(fs)) / fs->csize); /* Number of clusters - 1 */
+
+                               if (obj->stat == 2 && cofs <= clen) {   /* Is it a contiguous chain? */
+                                       val = (cofs == clen) ? 0x7FFFFFFF : clst + 1;   /* No data on the FAT, generate the value */
+                                       break;
+                               }
+                               if (obj->stat == 3 && cofs < obj->n_cont) {     /* Is it in the 1st fragment? */
+                                       val = clst + 1;         /* Generate the value */
+                                       break;
+                               }
+                               if (obj->stat != 2) {   /* Get value from FAT if FAT chain is valid */
+                                       if (obj->n_frag != 0) { /* Is it on the growing edge? */
+                                               val = 0x7FFFFFFF;       /* Generate EOC */
+                                       } else {
+                                               if (move_window(fs, fs->fatbase + (clst / (SS(fs) / 4))) != FR_OK) break;
+                                               val = ld_dword(fs->win + clst * 4 % SS(fs)) & 0x7FFFFFFF;
+                                       }
+                                       break;
+                               }
+                       }
+                       /* go to default */
+#endif
+               default:
+                       val = 1;        /* Internal error */
+               }
+       }
+
+       return val;
+}
+
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* FAT access - Change value of a FAT entry                              */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT put_fat (       /* FR_OK(0):succeeded, !=0:error */
+       FATFS* fs,              /* Corresponding filesystem object */
+       DWORD clst,             /* FAT index number (cluster number) to be changed */
+       DWORD val               /* New value to be set to the entry */
+)
+{
+       UINT bc;
+       BYTE *p;
+       FRESULT res = FR_INT_ERR;
+
+
+       if (clst >= 2 && clst < fs->n_fatent) { /* Check if in valid range */
+               switch (fs->fs_type) {
+               case FS_FAT12 :
+                       bc = (UINT)clst; bc += bc / 2;  /* bc: byte offset of the entry */
+                       res = move_window(fs, fs->fatbase + (bc / SS(fs)));
+                       if (res != FR_OK) break;
+                       p = fs->win + bc++ % SS(fs);
+                       *p = (clst & 1) ? ((*p & 0x0F) | ((BYTE)val << 4)) : (BYTE)val;         /* Update 1st byte */
+                       fs->wflag = 1;
+                       res = move_window(fs, fs->fatbase + (bc / SS(fs)));
+                       if (res != FR_OK) break;
+                       p = fs->win + bc % SS(fs);
+                       *p = (clst & 1) ? (BYTE)(val >> 4) : ((*p & 0xF0) | ((BYTE)(val >> 8) & 0x0F)); /* Update 2nd byte */
+                       fs->wflag = 1;
+                       break;
+
+               case FS_FAT16 :
+                       res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 2)));
+                       if (res != FR_OK) break;
+                       st_word(fs->win + clst * 2 % SS(fs), (WORD)val);        /* Simple WORD array */
+                       fs->wflag = 1;
+                       break;
+
+               case FS_FAT32 :
+#if FF_FS_EXFAT
+               case FS_EXFAT :
+#endif
+                       res = move_window(fs, fs->fatbase + (clst / (SS(fs) / 4)));
+                       if (res != FR_OK) break;
+                       if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) {
+                               val = (val & 0x0FFFFFFF) | (ld_dword(fs->win + clst * 4 % SS(fs)) & 0xF0000000);
+                       }
+                       st_dword(fs->win + clst * 4 % SS(fs), val);
+                       fs->wflag = 1;
+                       break;
+               }
+       }
+       return res;
+}
+
+#endif /* !FF_FS_READONLY */
+
+
+
+
+#if FF_FS_EXFAT && !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* exFAT: Accessing FAT and Allocation Bitmap                            */
+/*-----------------------------------------------------------------------*/
+
+/*--------------------------------------*/
+/* Find a contiguous free cluster block */
+/*--------------------------------------*/
+
+static DWORD find_bitmap (     /* 0:Not found, 2..:Cluster block found, 0xFFFFFFFF:Disk error */
+       FATFS* fs,      /* Filesystem object */
+       DWORD clst,     /* Cluster number to scan from */
+       DWORD ncl       /* Number of contiguous clusters to find (1..) */
+)
+{
+       BYTE bm, bv;
+       UINT i;
+       DWORD val, scl, ctr;
+
+
+       clst -= 2;      /* The first bit in the bitmap corresponds to cluster #2 */
+       if (clst >= fs->n_fatent - 2) clst = 0;
+       scl = val = clst; ctr = 0;
+       for (;;) {
+               if (move_window(fs, fs->bitbase + val / 8 / SS(fs)) != FR_OK) return 0xFFFFFFFF;
+               i = val / 8 % SS(fs); bm = 1 << (val % 8);
+               do {
+                       do {
+                               bv = fs->win[i] & bm; bm <<= 1;         /* Get bit value */
+                               if (++val >= fs->n_fatent - 2) {        /* Next cluster (with wrap-around) */
+                                       val = 0; bm = 0; i = SS(fs);
+                               }
+                               if (bv == 0) {  /* Is it a free cluster? */
+                                       if (++ctr == ncl) return scl + 2;       /* Check if run length is sufficient for required */
+                               } else {
+                                       scl = val; ctr = 0;             /* Encountered a cluster in-use, restart to scan */
+                               }
+                               if (val == clst) return 0;      /* All cluster scanned? */
+                       } while (bm != 0);
+                       bm = 1;
+               } while (++i < SS(fs));
+       }
+}
+
+
+/*----------------------------------------*/
+/* Set/Clear a block of allocation bitmap */
+/*----------------------------------------*/
+
+static FRESULT change_bitmap (
+       FATFS* fs,      /* Filesystem object */
+       DWORD clst,     /* Cluster number to change from */
+       DWORD ncl,      /* Number of clusters to be changed */
+       int bv          /* bit value to be set (0 or 1) */
+)
+{
+       BYTE bm;
+       UINT i;
+       LBA_t sect;
+
+
+       clst -= 2;      /* The first bit corresponds to cluster #2 */
+       sect = fs->bitbase + clst / 8 / SS(fs); /* Sector address */
+       i = clst / 8 % SS(fs);                                  /* Byte offset in the sector */
+       bm = 1 << (clst % 8);                                   /* Bit mask in the byte */
+       for (;;) {
+               if (move_window(fs, sect++) != FR_OK) return FR_DISK_ERR;
+               do {
+                       do {
+                               if (bv == (int)((fs->win[i] & bm) != 0)) return FR_INT_ERR;     /* Is the bit expected value? */
+                               fs->win[i] ^= bm;       /* Flip the bit */
+                               fs->wflag = 1;
+                               if (--ncl == 0) return FR_OK;   /* All bits processed? */
+                       } while (bm <<= 1);             /* Next bit */
+                       bm = 1;
+               } while (++i < SS(fs));         /* Next byte */
+               i = 0;
+       }
+}
+
+
+/*---------------------------------------------*/
+/* Fill the first fragment of the FAT chain    */
+/*---------------------------------------------*/
+
+static FRESULT fill_first_frag (
+       FFOBJID* obj    /* Pointer to the corresponding object */
+)
+{
+       FRESULT res;
+       DWORD cl, n;
+
+
+       if (obj->stat == 3) {   /* Has the object been changed 'fragmented' in this session? */
+               for (cl = obj->sclust, n = obj->n_cont; n; cl++, n--) { /* Create cluster chain on the FAT */
+                       res = put_fat(obj->fs, cl, cl + 1);
+                       if (res != FR_OK) return res;
+               }
+               obj->stat = 0;  /* Change status 'FAT chain is valid' */
+       }
+       return FR_OK;
+}
+
+
+/*---------------------------------------------*/
+/* Fill the last fragment of the FAT chain     */
+/*---------------------------------------------*/
+
+static FRESULT fill_last_frag (
+       FFOBJID* obj,   /* Pointer to the corresponding object */
+       DWORD lcl,              /* Last cluster of the fragment */
+       DWORD term              /* Value to set the last FAT entry */
+)
+{
+       FRESULT res;
+
+
+       while (obj->n_frag > 0) {       /* Create the chain of last fragment */
+               res = put_fat(obj->fs, lcl - obj->n_frag + 1, (obj->n_frag > 1) ? lcl - obj->n_frag + 2 : term);
+               if (res != FR_OK) return res;
+               obj->n_frag--;
+       }
+       return FR_OK;
+}
+
+#endif /* FF_FS_EXFAT && !FF_FS_READONLY */
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Remove a cluster chain                                 */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT remove_chain (  /* FR_OK(0):succeeded, !=0:error */
+       FFOBJID* obj,           /* Corresponding object */
+       DWORD clst,                     /* Cluster to remove a chain from */
+       DWORD pclst                     /* Previous cluster of clst (0 if entire chain) */
+)
+{
+       FRESULT res = FR_OK;
+       DWORD nxt;
+       FATFS *fs = obj->fs;
+#if FF_FS_EXFAT || FF_USE_TRIM
+       DWORD scl = clst, ecl = clst;
+#endif
+#if FF_USE_TRIM
+       LBA_t rt[2];
+#endif
+
+       if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR;        /* Check if in valid range */
+
+       /* Mark the previous cluster 'EOC' on the FAT if it exists */
+       if (pclst != 0 && (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT || obj->stat != 2)) {
+               res = put_fat(fs, pclst, 0xFFFFFFFF);
+               if (res != FR_OK) return res;
+       }
+
+       /* Remove the chain */
+       do {
+               nxt = get_fat(obj, clst);                       /* Get cluster status */
+               if (nxt == 0) break;                            /* Empty cluster? */
+               if (nxt == 1) return FR_INT_ERR;        /* Internal error? */
+               if (nxt == 0xFFFFFFFF) return FR_DISK_ERR;      /* Disk error? */
+               if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) {
+                       res = put_fat(fs, clst, 0);             /* Mark the cluster 'free' on the FAT */
+                       if (res != FR_OK) return res;
+               }
+               if (fs->free_clst < fs->n_fatent - 2) { /* Update FSINFO */
+                       fs->free_clst++;
+                       fs->fsi_flag |= 1;
+               }
+#if FF_FS_EXFAT || FF_USE_TRIM
+               if (ecl + 1 == nxt) {   /* Is next cluster contiguous? */
+                       ecl = nxt;
+               } else {                                /* End of contiguous cluster block */
+#if FF_FS_EXFAT
+                       if (fs->fs_type == FS_EXFAT) {
+                               res = change_bitmap(fs, scl, ecl - scl + 1, 0); /* Mark the cluster block 'free' on the bitmap */
+                               if (res != FR_OK) return res;
+                       }
+#endif
+#if FF_USE_TRIM
+                       rt[0] = clst2sect(fs, scl);                                     /* Start of data area to be freed */
+                       rt[1] = clst2sect(fs, ecl) + fs->csize - 1;     /* End of data area to be freed */
+                       disk_ioctl(fs->pdrv, CTRL_TRIM, rt);            /* Inform storage device that the data in the block may be erased */
+#endif
+                       scl = ecl = nxt;
+               }
+#endif
+               clst = nxt;                                     /* Next cluster */
+       } while (clst < fs->n_fatent);  /* Repeat while not the last link */
+
+#if FF_FS_EXFAT
+       /* Some post processes for chain status */
+       if (fs->fs_type == FS_EXFAT) {
+               if (pclst == 0) {       /* Has the entire chain been removed? */
+                       obj->stat = 0;          /* Change the chain status 'initial' */
+               } else {
+                       if (obj->stat == 0) {   /* Is it a fragmented chain from the beginning of this session? */
+                               clst = obj->sclust;             /* Follow the chain to check if it gets contiguous */
+                               while (clst != pclst) {
+                                       nxt = get_fat(obj, clst);
+                                       if (nxt < 2) return FR_INT_ERR;
+                                       if (nxt == 0xFFFFFFFF) return FR_DISK_ERR;
+                                       if (nxt != clst + 1) break;     /* Not contiguous? */
+                                       clst++;
+                               }
+                               if (clst == pclst) {    /* Has the chain got contiguous again? */
+                                       obj->stat = 2;          /* Change the chain status 'contiguous' */
+                               }
+                       } else {
+                               if (obj->stat == 3 && pclst >= obj->sclust && pclst <= obj->sclust + obj->n_cont) {     /* Was the chain fragmented in this session and got contiguous again? */
+                                       obj->stat = 2;  /* Change the chain status 'contiguous' */
+                               }
+                       }
+               }
+       }
+#endif
+       return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Stretch a chain or Create a new chain                  */
+/*-----------------------------------------------------------------------*/
+
+static DWORD create_chain (    /* 0:No free cluster, 1:Internal error, 0xFFFFFFFF:Disk error, >=2:New cluster# */
+       FFOBJID* obj,           /* Corresponding object */
+       DWORD clst                      /* Cluster# to stretch, 0:Create a new chain */
+)
+{
+       DWORD cs, ncl, scl;
+       FRESULT res;
+       FATFS *fs = obj->fs;
+
+
+       if (clst == 0) {        /* Create a new chain */
+               scl = fs->last_clst;                            /* Suggested cluster to start to find */
+               if (scl == 0 || scl >= fs->n_fatent) scl = 1;
+       }
+       else {                          /* Stretch a chain */
+               cs = get_fat(obj, clst);                        /* Check the cluster status */
+               if (cs < 2) return 1;                           /* Test for insanity */
+               if (cs == 0xFFFFFFFF) return cs;        /* Test for disk error */
+               if (cs < fs->n_fatent) return cs;       /* It is already followed by next cluster */
+               scl = clst;                                                     /* Cluster to start to find */
+       }
+       if (fs->free_clst == 0) return 0;               /* No free cluster */
+
+#if FF_FS_EXFAT
+       if (fs->fs_type == FS_EXFAT) {  /* On the exFAT volume */
+               ncl = find_bitmap(fs, scl, 1);                          /* Find a free cluster */
+               if (ncl == 0 || ncl == 0xFFFFFFFF) return ncl;  /* No free cluster or hard error? */
+               res = change_bitmap(fs, ncl, 1, 1);                     /* Mark the cluster 'in use' */
+               if (res == FR_INT_ERR) return 1;
+               if (res == FR_DISK_ERR) return 0xFFFFFFFF;
+               if (clst == 0) {                                                        /* Is it a new chain? */
+                       obj->stat = 2;                                                  /* Set status 'contiguous' */
+               } else {                                                                        /* It is a stretched chain */
+                       if (obj->stat == 2 && ncl != scl + 1) { /* Is the chain got fragmented? */
+                               obj->n_cont = scl - obj->sclust;        /* Set size of the contiguous part */
+                               obj->stat = 3;                                          /* Change status 'just fragmented' */
+                       }
+               }
+               if (obj->stat != 2) {   /* Is the file non-contiguous? */
+                       if (ncl == clst + 1) {  /* Is the cluster next to previous one? */
+                               obj->n_frag = obj->n_frag ? obj->n_frag + 1 : 2;        /* Increment size of last framgent */
+                       } else {                                /* New fragment */
+                               if (obj->n_frag == 0) obj->n_frag = 1;
+                               res = fill_last_frag(obj, clst, ncl);   /* Fill last fragment on the FAT and link it to new one */
+                               if (res == FR_OK) obj->n_frag = 1;
+                       }
+               }
+       } else
+#endif
+       {       /* On the FAT/FAT32 volume */
+               ncl = 0;
+               if (scl == clst) {                                              /* Stretching an existing chain? */
+                       ncl = scl + 1;                                          /* Test if next cluster is free */
+                       if (ncl >= fs->n_fatent) ncl = 2;
+                       cs = get_fat(obj, ncl);                         /* Get next cluster status */
+                       if (cs == 1 || cs == 0xFFFFFFFF) return cs;     /* Test for error */
+                       if (cs != 0) {                                          /* Not free? */
+                               cs = fs->last_clst;                             /* Start at suggested cluster if it is valid */
+                               if (cs >= 2 && cs < fs->n_fatent) scl = cs;
+                               ncl = 0;
+                       }
+               }
+               if (ncl == 0) { /* The new cluster cannot be contiguous and find another fragment */
+                       ncl = scl;      /* Start cluster */
+                       for (;;) {
+                               ncl++;                                                  /* Next cluster */
+                               if (ncl >= fs->n_fatent) {              /* Check wrap-around */
+                                       ncl = 2;
+                                       if (ncl > scl) return 0;        /* No free cluster found? */
+                               }
+                               cs = get_fat(obj, ncl);                 /* Get the cluster status */
+                               if (cs == 0) break;                             /* Found a free cluster? */
+                               if (cs == 1 || cs == 0xFFFFFFFF) return cs;     /* Test for error */
+                               if (ncl == scl) return 0;               /* No free cluster found? */
+                       }
+               }
+               res = put_fat(fs, ncl, 0xFFFFFFFF);             /* Mark the new cluster 'EOC' */
+               if (res == FR_OK && clst != 0) {
+                       res = put_fat(fs, clst, ncl);           /* Link it from the previous one if needed */
+               }
+       }
+
+       if (res == FR_OK) {                     /* Update FSINFO if function succeeded. */
+               fs->last_clst = ncl;
+               if (fs->free_clst <= fs->n_fatent - 2) fs->free_clst--;
+               fs->fsi_flag |= 1;
+       } else {
+               ncl = (res == FR_DISK_ERR) ? 0xFFFFFFFF : 1;    /* Failed. Generate error status */
+       }
+
+       return ncl;             /* Return new cluster number or error status */
+}
+
+#endif /* !FF_FS_READONLY */
+
+
+
+
+#if FF_USE_FASTSEEK
+/*-----------------------------------------------------------------------*/
+/* FAT handling - Convert offset into cluster with link map table        */
+/*-----------------------------------------------------------------------*/
+
+static DWORD clmt_clust (      /* <2:Error, >=2:Cluster number */
+       FIL* fp,                /* Pointer to the file object */
+       FSIZE_t ofs             /* File offset to be converted to cluster# */
+)
+{
+       DWORD cl, ncl, *tbl;
+       FATFS *fs = fp->obj.fs;
+
+
+       tbl = fp->cltbl + 1;    /* Top of CLMT */
+       cl = (DWORD)(ofs / SS(fs) / fs->csize); /* Cluster order from top of the file */
+       for (;;) {
+               ncl = *tbl++;                   /* Number of cluters in the fragment */
+               if (ncl == 0) return 0; /* End of table? (error) */
+               if (cl < ncl) break;    /* In this fragment? */
+               cl -= ncl; tbl++;               /* Next fragment */
+       }
+       return cl + *tbl;       /* Return the cluster number */
+}
+
+#endif /* FF_USE_FASTSEEK */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Fill a cluster with zeros                        */
+/*-----------------------------------------------------------------------*/
+
+#if !FF_FS_READONLY
+static FRESULT dir_clear (     /* Returns FR_OK or FR_DISK_ERR */
+       FATFS *fs,              /* Filesystem object */
+       DWORD clst              /* Directory table to clear */
+)
+{
+       LBA_t sect;
+       UINT n, szb;
+       BYTE *ibuf;
+
+
+       if (sync_window(fs) != FR_OK) return FR_DISK_ERR;       /* Flush disk access window */
+       sect = clst2sect(fs, clst);             /* Top of the cluster */
+       fs->winsect = sect;                             /* Set window to top of the cluster */
+       mem_set(fs->win, 0, sizeof fs->win);    /* Clear window buffer */
+#if FF_USE_LFN == 3            /* Quick table clear by using multi-secter write */
+       /* Allocate a temporary buffer */
+       for (szb = ((DWORD)fs->csize * SS(fs) >= MAX_MALLOC) ? MAX_MALLOC : fs->csize * SS(fs), ibuf = 0; szb > SS(fs) && (ibuf = ff_memalloc(szb)) == 0; szb /= 2) ;
+       if (szb > SS(fs)) {             /* Buffer allocated? */
+               mem_set(ibuf, 0, szb);
+               szb /= SS(fs);          /* Bytes -> Sectors */
+               for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) ;   /* Fill the cluster with 0 */
+               ff_memfree(ibuf);
+       } else
+#endif
+       {
+               ibuf = fs->win; szb = 1;        /* Use window buffer (many single-sector writes may take a time) */
+               for (n = 0; n < fs->csize && disk_write(fs->pdrv, ibuf, sect + n, szb) == RES_OK; n += szb) ;   /* Fill the cluster with 0 */
+       }
+       return (n == fs->csize) ? FR_OK : FR_DISK_ERR;
+}
+#endif /* !FF_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Set directory index                              */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT dir_sdi (       /* FR_OK(0):succeeded, !=0:error */
+       DIR* dp,                /* Pointer to directory object */
+       DWORD ofs               /* Offset of directory table */
+)
+{
+       DWORD csz, clst;
+       FATFS *fs = dp->obj.fs;
+
+
+       if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR) || ofs % SZDIRE) {  /* Check range of offset and alignment */
+               return FR_INT_ERR;
+       }
+       dp->dptr = ofs;                         /* Set current offset */
+       clst = dp->obj.sclust;          /* Table start cluster (0:root) */
+       if (clst == 0 && fs->fs_type >= FS_FAT32) {     /* Replace cluster# 0 with root cluster# */
+               clst = (DWORD)fs->dirbase;
+               if (FF_FS_EXFAT) dp->obj.stat = 0;      /* exFAT: Root dir has an FAT chain */
+       }
+
+       if (clst == 0) {        /* Static table (root-directory on the FAT volume) */
+               if (ofs / SZDIRE >= fs->n_rootdir) return FR_INT_ERR;   /* Is index out of range? */
+               dp->sect = fs->dirbase;
+
+       } else {                        /* Dynamic table (sub-directory or root-directory on the FAT32/exFAT volume) */
+               csz = (DWORD)fs->csize * SS(fs);        /* Bytes per cluster */
+               while (ofs >= csz) {                            /* Follow cluster chain */
+                       clst = get_fat(&dp->obj, clst);                         /* Get next cluster */
+                       if (clst == 0xFFFFFFFF) return FR_DISK_ERR;     /* Disk error */
+                       if (clst < 2 || clst >= fs->n_fatent) return FR_INT_ERR;        /* Reached to end of table or internal error */
+                       ofs -= csz;
+               }
+               dp->sect = clst2sect(fs, clst);
+       }
+       dp->clust = clst;                                       /* Current cluster# */
+       if (dp->sect == 0) return FR_INT_ERR;
+       dp->sect += ofs / SS(fs);                       /* Sector# of the directory entry */
+       dp->dir = fs->win + (ofs % SS(fs));     /* Pointer to the entry in the win[] */
+
+       return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Move directory table index next                  */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT dir_next (      /* FR_OK(0):succeeded, FR_NO_FILE:End of table, FR_DENIED:Could not stretch */
+       DIR* dp,                                /* Pointer to the directory object */
+       int stretch                             /* 0: Do not stretch table, 1: Stretch table if needed */
+)
+{
+       DWORD ofs, clst;
+       FATFS *fs = dp->obj.fs;
+
+
+       ofs = dp->dptr + SZDIRE;        /* Next entry */
+       if (ofs >= (DWORD)((FF_FS_EXFAT && fs->fs_type == FS_EXFAT) ? MAX_DIR_EX : MAX_DIR)) dp->sect = 0;      /* Disable it if the offset reached the max value */
+       if (dp->sect == 0) return FR_NO_FILE;   /* Report EOT if it has been disabled */
+
+       if (ofs % SS(fs) == 0) {        /* Sector changed? */
+               dp->sect++;                             /* Next sector */
+
+               if (dp->clust == 0) {   /* Static table */
+                       if (ofs / SZDIRE >= fs->n_rootdir) {    /* Report EOT if it reached end of static table */
+                               dp->sect = 0; return FR_NO_FILE;
+                       }
+               }
+               else {                                  /* Dynamic table */
+                       if ((ofs / SS(fs) & (fs->csize - 1)) == 0) {    /* Cluster changed? */
+                               clst = get_fat(&dp->obj, dp->clust);            /* Get next cluster */
+                               if (clst <= 1) return FR_INT_ERR;                       /* Internal error */
+                               if (clst == 0xFFFFFFFF) return FR_DISK_ERR;     /* Disk error */
+                               if (clst >= fs->n_fatent) {                                     /* It reached end of dynamic table */
+#if !FF_FS_READONLY
+                                       if (!stretch) {                                                         /* If no stretch, report EOT */
+                                               dp->sect = 0; return FR_NO_FILE;
+                                       }
+                                       clst = create_chain(&dp->obj, dp->clust);       /* Allocate a cluster */
+                                       if (clst == 0) return FR_DENIED;                        /* No free cluster */
+                                       if (clst == 1) return FR_INT_ERR;                       /* Internal error */
+                                       if (clst == 0xFFFFFFFF) return FR_DISK_ERR;     /* Disk error */
+                                       if (dir_clear(fs, clst) != FR_OK) return FR_DISK_ERR;   /* Clean up the stretched table */
+                                       if (FF_FS_EXFAT) dp->obj.stat |= 4;                     /* exFAT: The directory has been stretched */
+#else
+                                       if (!stretch) dp->sect = 0;                                     /* (this line is to suppress compiler warning) */
+                                       dp->sect = 0; return FR_NO_FILE;                        /* Report EOT */
+#endif
+                               }
+                               dp->clust = clst;               /* Initialize data for new cluster */
+                               dp->sect = clst2sect(fs, clst);
+                       }
+               }
+       }
+       dp->dptr = ofs;                                         /* Current entry */
+       dp->dir = fs->win + ofs % SS(fs);       /* Pointer to the entry in the win[] */
+
+       return FR_OK;
+}
+
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Reserve a block of directory entries             */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT dir_alloc (     /* FR_OK(0):succeeded, !=0:error */
+       DIR* dp,                                /* Pointer to the directory object */
+       UINT nent                               /* Number of contiguous entries to allocate */
+)
+{
+       FRESULT res;
+       UINT n;
+       FATFS *fs = dp->obj.fs;
+
+
+       res = dir_sdi(dp, 0);
+       if (res == FR_OK) {
+               n = 0;
+               do {
+                       res = move_window(fs, dp->sect);
+                       if (res != FR_OK) break;
+#if FF_FS_EXFAT
+                       if ((fs->fs_type == FS_EXFAT) ? (int)((dp->dir[XDIR_Type] & 0x80) == 0) : (int)(dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0)) {
+#else
+                       if (dp->dir[DIR_Name] == DDEM || dp->dir[DIR_Name] == 0) {
+#endif
+                               if (++n == nent) break; /* A block of contiguous free entries is found */
+                       } else {
+                               n = 0;                                  /* Not a blank entry. Restart to search */
+                       }
+                       res = dir_next(dp, 1);
+               } while (res == FR_OK); /* Next entry with table stretch enabled */
+       }
+
+       if (res == FR_NO_FILE) res = FR_DENIED; /* No directory entry to allocate */
+       return res;
+}
+
+#endif /* !FF_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* FAT: Directory handling - Load/Store start cluster number             */
+/*-----------------------------------------------------------------------*/
+
+static DWORD ld_clust (        /* Returns the top cluster value of the SFN entry */
+       FATFS* fs,                      /* Pointer to the fs object */
+       const BYTE* dir         /* Pointer to the key entry */
+)
+{
+       DWORD cl;
+
+       cl = ld_word(dir + DIR_FstClusLO);
+       if (fs->fs_type == FS_FAT32) {
+               cl |= (DWORD)ld_word(dir + DIR_FstClusHI) << 16;
+       }
+
+       return cl;
+}
+
+
+#if !FF_FS_READONLY
+static void st_clust (
+       FATFS* fs,      /* Pointer to the fs object */
+       BYTE* dir,      /* Pointer to the key entry */
+       DWORD cl        /* Value to be set */
+)
+{
+       st_word(dir + DIR_FstClusLO, (WORD)cl);
+       if (fs->fs_type == FS_FAT32) {
+               st_word(dir + DIR_FstClusHI, (WORD)(cl >> 16));
+       }
+}
+#endif
+
+
+
+#if FF_USE_LFN
+/*--------------------------------------------------------*/
+/* FAT-LFN: Compare a part of file name with an LFN entry */
+/*--------------------------------------------------------*/
+
+static int cmp_lfn (           /* 1:matched, 0:not matched */
+       const WCHAR* lfnbuf,    /* Pointer to the LFN working buffer to be compared */
+       BYTE* dir                               /* Pointer to the directory entry containing the part of LFN */
+)
+{
+       UINT i, s;
+       WCHAR wc, uc;
+
+
+       if (ld_word(dir + LDIR_FstClusLO) != 0) return 0;       /* Check LDIR_FstClusLO */
+
+       i = ((dir[LDIR_Ord] & 0x3F) - 1) * 13;  /* Offset in the LFN buffer */
+
+       for (wc = 1, s = 0; s < 13; s++) {              /* Process all characters in the entry */
+               uc = ld_word(dir + LfnOfs[s]);          /* Pick an LFN character */
+               if (wc != 0) {
+                       if (i >= FF_MAX_LFN + 1 || ff_wtoupper(uc) != ff_wtoupper(lfnbuf[i++])) {       /* Compare it */
+                               return 0;                                       /* Not matched */
+                       }
+                       wc = uc;
+               } else {
+                       if (uc != 0xFFFF) return 0;             /* Check filler */
+               }
+       }
+
+       if ((dir[LDIR_Ord] & LLEF) && wc && lfnbuf[i]) return 0;        /* Last segment matched but different length */
+
+       return 1;               /* The part of LFN matched */
+}
+
+
+#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
+/*-----------------------------------------------------*/
+/* FAT-LFN: Pick a part of file name from an LFN entry */
+/*-----------------------------------------------------*/
+
+static int pick_lfn (  /* 1:succeeded, 0:buffer overflow or invalid LFN entry */
+       WCHAR* lfnbuf,          /* Pointer to the LFN working buffer */
+       BYTE* dir                       /* Pointer to the LFN entry */
+)
+{
+       UINT i, s;
+       WCHAR wc, uc;
+
+
+       if (ld_word(dir + LDIR_FstClusLO) != 0) return 0;       /* Check LDIR_FstClusLO is 0 */
+
+       i = ((dir[LDIR_Ord] & ~LLEF) - 1) * 13; /* Offset in the LFN buffer */
+
+       for (wc = 1, s = 0; s < 13; s++) {              /* Process all characters in the entry */
+               uc = ld_word(dir + LfnOfs[s]);          /* Pick an LFN character */
+               if (wc != 0) {
+                       if (i >= FF_MAX_LFN + 1) return 0;      /* Buffer overflow? */
+                       lfnbuf[i++] = wc = uc;                  /* Store it */
+               } else {
+                       if (uc != 0xFFFF) return 0;             /* Check filler */
+               }
+       }
+
+       if (dir[LDIR_Ord] & LLEF && wc != 0) {  /* Put terminator if it is the last LFN part and not terminated */
+               if (i >= FF_MAX_LFN + 1) return 0;      /* Buffer overflow? */
+               lfnbuf[i] = 0;
+       }
+
+       return 1;               /* The part of LFN is valid */
+}
+#endif
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------*/
+/* FAT-LFN: Create an entry of LFN entries */
+/*-----------------------------------------*/
+
+static void put_lfn (
+       const WCHAR* lfn,       /* Pointer to the LFN */
+       BYTE* dir,                      /* Pointer to the LFN entry to be created */
+       BYTE ord,                       /* LFN order (1-20) */
+       BYTE sum                        /* Checksum of the corresponding SFN */
+)
+{
+       UINT i, s;
+       WCHAR wc;
+
+
+       dir[LDIR_Chksum] = sum;                 /* Set checksum */
+       dir[LDIR_Attr] = AM_LFN;                /* Set attribute. LFN entry */
+       dir[LDIR_Type] = 0;
+       st_word(dir + LDIR_FstClusLO, 0);
+
+       i = (ord - 1) * 13;                             /* Get offset in the LFN working buffer */
+       s = wc = 0;
+       do {
+               if (wc != 0xFFFF) wc = lfn[i++];        /* Get an effective character */
+               st_word(dir + LfnOfs[s], wc);           /* Put it */
+               if (wc == 0) wc = 0xFFFF;               /* Padding characters for following items */
+       } while (++s < 13);
+       if (wc == 0xFFFF || !lfn[i]) ord |= LLEF;       /* Last LFN part is the start of LFN sequence */
+       dir[LDIR_Ord] = ord;                    /* Set the LFN order */
+}
+
+#endif /* !FF_FS_READONLY */
+#endif /* FF_USE_LFN */
+
+
+
+#if FF_USE_LFN && !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* FAT-LFN: Create a Numbered SFN                                        */
+/*-----------------------------------------------------------------------*/
+
+static void gen_numname (
+       BYTE* dst,                      /* Pointer to the buffer to store numbered SFN */
+       const BYTE* src,        /* Pointer to SFN */
+       const WCHAR* lfn,       /* Pointer to LFN */
+       UINT seq                        /* Sequence number */
+)
+{
+       BYTE ns[8], c;
+       UINT i, j;
+       WCHAR wc;
+       DWORD sreg;
+
+
+       mem_cpy(dst, src, 11);
+
+       if (seq > 5) {  /* In case of many collisions, generate a hash number instead of sequential number */
+               sreg = seq;
+               while (*lfn) {  /* Create a CRC as hash value */
+                       wc = *lfn++;
+                       for (i = 0; i < 16; i++) {
+                               sreg = (sreg << 1) + (wc & 1);
+                               wc >>= 1;
+                               if (sreg & 0x10000) sreg ^= 0x11021;
+                       }
+               }
+               seq = (UINT)sreg;
+       }
+
+       /* itoa (hexdecimal) */
+       i = 7;
+       do {
+               c = (BYTE)((seq % 16) + '0');
+               if (c > '9') c += 7;
+               ns[i--] = c;
+               seq /= 16;
+       } while (seq);
+       ns[i] = '~';
+
+       /* Append the number to the SFN body */
+       for (j = 0; j < i && dst[j] != ' '; j++) {
+               if (dbc_1st(dst[j])) {
+                       if (j == i - 1) break;
+                       j++;
+               }
+       }
+       do {
+               dst[j++] = (i < 8) ? ns[i++] : ' ';
+       } while (j < 8);
+}
+#endif /* FF_USE_LFN && !FF_FS_READONLY */
+
+
+
+#if FF_USE_LFN
+/*-----------------------------------------------------------------------*/
+/* FAT-LFN: Calculate checksum of an SFN entry                           */
+/*-----------------------------------------------------------------------*/
+
+static BYTE sum_sfn (
+       const BYTE* dir         /* Pointer to the SFN entry */
+)
+{
+       BYTE sum = 0;
+       UINT n = 11;
+
+       do {
+               sum = (sum >> 1) + (sum << 7) + *dir++;
+       } while (--n);
+       return sum;
+}
+
+#endif /* FF_USE_LFN */
+
+
+
+#if FF_FS_EXFAT
+/*-----------------------------------------------------------------------*/
+/* exFAT: Checksum                                                       */
+/*-----------------------------------------------------------------------*/
+
+static WORD xdir_sum ( /* Get checksum of the directoly entry block */
+       const BYTE* dir         /* Directory entry block to be calculated */
+)
+{
+       UINT i, szblk;
+       WORD sum;
+
+
+       szblk = (dir[XDIR_NumSec] + 1) * SZDIRE;        /* Number of bytes of the entry block */
+       for (i = sum = 0; i < szblk; i++) {
+               if (i == XDIR_SetSum) { /* Skip 2-byte sum field */
+                       i++;
+               } else {
+                       sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + dir[i];
+               }
+       }
+       return sum;
+}
+
+
+
+static WORD xname_sum (        /* Get check sum (to be used as hash) of the file name */
+       const WCHAR* name       /* File name to be calculated */
+)
+{
+       WCHAR chr;
+       WORD sum = 0;
+
+
+       while ((chr = *name++) != 0) {
+               chr = (WCHAR)ff_wtoupper(chr);          /* File name needs to be up-case converted */
+               sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr & 0xFF);
+               sum = ((sum & 1) ? 0x8000 : 0) + (sum >> 1) + (chr >> 8);
+       }
+       return sum;
+}
+
+
+#if !FF_FS_READONLY && FF_USE_MKFS
+static DWORD xsum32 (  /* Returns 32-bit checksum */
+       BYTE  dat,                      /* Byte to be calculated (byte-by-byte processing) */
+       DWORD sum                       /* Previous sum value */
+)
+{
+       sum = ((sum & 1) ? 0x80000000 : 0) + (sum >> 1) + dat;
+       return sum;
+}
+#endif
+
+
+#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2
+/*------------------------------------------------------*/
+/* exFAT: Get object information from a directory block */
+/*------------------------------------------------------*/
+
+static void get_xfileinfo (
+       BYTE* dirb,                     /* Pointer to the direcotry entry block 85+C0+C1s */
+       FILINFO* fno            /* Buffer to store the extracted file information */
+)
+{
+       WCHAR wc, hs;
+       UINT di, si, nc;
+
+       /* Get file name from the entry block */
+       si = SZDIRE * 2;        /* 1st C1 entry */
+       nc = 0; hs = 0; di = 0;
+       while (nc < dirb[XDIR_NumName]) {
+               if (si >= MAXDIRB(FF_MAX_LFN)) { di = 0; break; }       /* Truncated directory block? */
+               if ((si % SZDIRE) == 0) si += 2;                /* Skip entry type field */
+               wc = ld_word(dirb + si); si += 2; nc++; /* Get a character */
+               if (hs == 0 && IsSurrogate(wc)) {       /* Is it a surrogate? */
+                       hs = wc; continue;      /* Get low surrogate */
+               }
+               wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di);   /* Store it in API encoding */
+               if (wc == 0) { di = 0; break; } /* Buffer overflow or wrong encoding? */
+               di += wc;
+               hs = 0;
+       }
+       if (hs != 0) di = 0;                                    /* Broken surrogate pair? */
+       if (di == 0) fno->fname[di++] = '?';    /* Inaccessible object name? */
+       fno->fname[di] = 0;                                             /* Terminate the name */
+       fno->altname[0] = 0;                                    /* exFAT does not support SFN */
+
+       fno->fattrib = dirb[XDIR_Attr];                 /* Attribute */
+       fno->fsize = (fno->fattrib & AM_DIR) ? 0 : ld_qword(dirb + XDIR_FileSize);      /* Size */
+       fno->ftime = ld_word(dirb + XDIR_ModTime + 0);  /* Time */
+       fno->fdate = ld_word(dirb + XDIR_ModTime + 2);  /* Date */
+}
+
+#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
+
+
+/*-----------------------------------*/
+/* exFAT: Get a directry entry block */
+/*-----------------------------------*/
+
+static FRESULT load_xdir (     /* FR_INT_ERR: invalid entry block */
+       DIR* dp                                 /* Reading direcotry object pointing top of the entry block to load */
+)
+{
+       FRESULT res;
+       UINT i, sz_ent;
+       BYTE* dirb = dp->obj.fs->dirbuf;        /* Pointer to the on-memory direcotry entry block 85+C0+C1s */
+
+
+       /* Load file-directory entry */
+       res = move_window(dp->obj.fs, dp->sect);
+       if (res != FR_OK) return res;
+       if (dp->dir[XDIR_Type] != ET_FILEDIR) return FR_INT_ERR;        /* Invalid order */
+       mem_cpy(dirb + 0 * SZDIRE, dp->dir, SZDIRE);
+       sz_ent = (dirb[XDIR_NumSec] + 1) * SZDIRE;
+       if (sz_ent < 3 * SZDIRE || sz_ent > 19 * SZDIRE) return FR_INT_ERR;
+
+       /* Load stream-extension entry */
+       res = dir_next(dp, 0);
+       if (res == FR_NO_FILE) res = FR_INT_ERR;        /* It cannot be */
+       if (res != FR_OK) return res;
+       res = move_window(dp->obj.fs, dp->sect);
+       if (res != FR_OK) return res;
+       if (dp->dir[XDIR_Type] != ET_STREAM) return FR_INT_ERR; /* Invalid order */
+       mem_cpy(dirb + 1 * SZDIRE, dp->dir, SZDIRE);
+       if (MAXDIRB(dirb[XDIR_NumName]) > sz_ent) return FR_INT_ERR;
+
+       /* Load file-name entries */
+       i = 2 * SZDIRE; /* Name offset to load */
+       do {
+               res = dir_next(dp, 0);
+               if (res == FR_NO_FILE) res = FR_INT_ERR;        /* It cannot be */
+               if (res != FR_OK) return res;
+               res = move_window(dp->obj.fs, dp->sect);
+               if (res != FR_OK) return res;
+               if (dp->dir[XDIR_Type] != ET_FILENAME) return FR_INT_ERR;       /* Invalid order */
+               if (i < MAXDIRB(FF_MAX_LFN)) mem_cpy(dirb + i, dp->dir, SZDIRE);
+       } while ((i += SZDIRE) < sz_ent);
+
+       /* Sanity check (do it for only accessible object) */
+       if (i <= MAXDIRB(FF_MAX_LFN)) {
+               if (xdir_sum(dirb) != ld_word(dirb + XDIR_SetSum)) return FR_INT_ERR;
+       }
+       return FR_OK;
+}
+
+
+/*------------------------------------------------------------------*/
+/* exFAT: Initialize object allocation info with loaded entry block */
+/*------------------------------------------------------------------*/
+
+static void init_alloc_info (
+       FATFS* fs,              /* Filesystem object */
+       FFOBJID* obj    /* Object allocation information to be initialized */
+)
+{
+       obj->sclust = ld_dword(fs->dirbuf + XDIR_FstClus);              /* Start cluster */
+       obj->objsize = ld_qword(fs->dirbuf + XDIR_FileSize);    /* Size */
+       obj->stat = fs->dirbuf[XDIR_GenFlags] & 2;                              /* Allocation status */
+       obj->n_frag = 0;                                                                                /* No last fragment info */
+}
+
+
+
+#if !FF_FS_READONLY || FF_FS_RPATH != 0
+/*------------------------------------------------*/
+/* exFAT: Load the object's directory entry block */
+/*------------------------------------------------*/
+
+static FRESULT load_obj_xdir (
+       DIR* dp,                        /* Blank directory object to be used to access containing direcotry */
+       const FFOBJID* obj      /* Object with its containing directory information */
+)
+{
+       FRESULT res;
+
+       /* Open object containing directory */
+       dp->obj.fs = obj->fs;
+       dp->obj.sclust = obj->c_scl;
+       dp->obj.stat = (BYTE)obj->c_size;
+       dp->obj.objsize = obj->c_size & 0xFFFFFF00;
+       dp->obj.n_frag = 0;
+       dp->blk_ofs = obj->c_ofs;
+
+       res = dir_sdi(dp, dp->blk_ofs); /* Goto object's entry block */
+       if (res == FR_OK) {
+               res = load_xdir(dp);            /* Load the object's entry block */
+       }
+       return res;
+}
+#endif
+
+
+#if !FF_FS_READONLY
+/*----------------------------------------*/
+/* exFAT: Store the directory entry block */
+/*----------------------------------------*/
+
+static FRESULT store_xdir (
+       DIR* dp                         /* Pointer to the direcotry object */
+)
+{
+       FRESULT res;
+       UINT nent;
+       BYTE* dirb = dp->obj.fs->dirbuf;        /* Pointer to the direcotry entry block 85+C0+C1s */
+
+       /* Create set sum */
+       st_word(dirb + XDIR_SetSum, xdir_sum(dirb));
+       nent = dirb[XDIR_NumSec] + 1;
+
+       /* Store the direcotry entry block to the directory */
+       res = dir_sdi(dp, dp->blk_ofs);
+       while (res == FR_OK) {
+               res = move_window(dp->obj.fs, dp->sect);
+               if (res != FR_OK) break;
+               mem_cpy(dp->dir, dirb, SZDIRE);
+               dp->obj.fs->wflag = 1;
+               if (--nent == 0) break;
+               dirb += SZDIRE;
+               res = dir_next(dp, 0);
+       }
+       return (res == FR_OK || res == FR_DISK_ERR) ? res : FR_INT_ERR;
+}
+
+
+
+/*-------------------------------------------*/
+/* exFAT: Create a new directory enrty block */
+/*-------------------------------------------*/
+
+static void create_xdir (
+       BYTE* dirb,                     /* Pointer to the direcotry entry block buffer */
+       const WCHAR* lfn        /* Pointer to the object name */
+)
+{
+       UINT i;
+       BYTE nc1, nlen;
+       WCHAR wc;
+
+
+       /* Create file-directory and stream-extension entry */
+       mem_set(dirb, 0, 2 * SZDIRE);
+       dirb[0 * SZDIRE + XDIR_Type] = ET_FILEDIR;
+       dirb[1 * SZDIRE + XDIR_Type] = ET_STREAM;
+
+       /* Create file-name entries */
+       i = SZDIRE * 2; /* Top of file_name entries */
+       nlen = nc1 = 0; wc = 1;
+       do {
+               dirb[i++] = ET_FILENAME; dirb[i++] = 0;
+               do {    /* Fill name field */
+                       if (wc != 0 && (wc = lfn[nlen]) != 0) nlen++;   /* Get a character if exist */
+                       st_word(dirb + i, wc);          /* Store it */
+                       i += 2;
+               } while (i % SZDIRE != 0);
+               nc1++;
+       } while (lfn[nlen]);    /* Fill next entry if any char follows */
+
+       dirb[XDIR_NumName] = nlen;              /* Set name length */
+       dirb[XDIR_NumSec] = 1 + nc1;    /* Set secondary count (C0 + C1s) */
+       st_word(dirb + XDIR_NameHash, xname_sum(lfn));  /* Set name hash */
+}
+
+#endif /* !FF_FS_READONLY */
+#endif /* FF_FS_EXFAT */
+
+
+
+#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 || FF_USE_LABEL || FF_FS_EXFAT
+/*-----------------------------------------------------------------------*/
+/* Read an object from the directory                                     */
+/*-----------------------------------------------------------------------*/
+
+#define DIR_READ_FILE(dp) dir_read(dp, 0)
+#define DIR_READ_LABEL(dp) dir_read(dp, 1)
+
+static FRESULT dir_read (
+       DIR* dp,                /* Pointer to the directory object */
+       int vol                 /* Filtered by 0:file/directory or 1:volume label */
+)
+{
+       FRESULT res = FR_NO_FILE;
+       FATFS *fs = dp->obj.fs;
+       BYTE attr, b;
+#if FF_USE_LFN
+       BYTE ord = 0xFF, sum = 0xFF;
+#endif
+
+       while (dp->sect) {
+               res = move_window(fs, dp->sect);
+               if (res != FR_OK) break;
+               b = dp->dir[DIR_Name];  /* Test for the entry type */
+               if (b == 0) {
+                       res = FR_NO_FILE; break; /* Reached to end of the directory */
+               }
+#if FF_FS_EXFAT
+               if (fs->fs_type == FS_EXFAT) {  /* On the exFAT volume */
+                       if (FF_USE_LABEL && vol) {
+                               if (b == ET_VLABEL) break;      /* Volume label entry? */
+                       } else {
+                               if (b == ET_FILEDIR) {          /* Start of the file entry block? */
+                                       dp->blk_ofs = dp->dptr; /* Get location of the block */
+                                       res = load_xdir(dp);    /* Load the entry block */
+                                       if (res == FR_OK) {
+                                               dp->obj.attr = fs->dirbuf[XDIR_Attr] & AM_MASK; /* Get attribute */
+                                       }
+                                       break;
+                               }
+                       }
+               } else
+#endif
+               {       /* On the FAT/FAT32 volume */
+                       dp->obj.attr = attr = dp->dir[DIR_Attr] & AM_MASK;      /* Get attribute */
+#if FF_USE_LFN         /* LFN configuration */
+                       if (b == DDEM || b == '.' || (int)((attr & ~AM_ARC) == AM_VOL) != vol) {        /* An entry without valid data */
+                               ord = 0xFF;
+                       } else {
+                               if (attr == AM_LFN) {                   /* An LFN entry is found */
+                                       if (b & LLEF) {                 /* Is it start of an LFN sequence? */
+                                               sum = dp->dir[LDIR_Chksum];
+                                               b &= (BYTE)~LLEF; ord = b;
+                                               dp->blk_ofs = dp->dptr;
+                                       }
+                                       /* Check LFN validity and capture it */
+                                       ord = (b == ord && sum == dp->dir[LDIR_Chksum] && pick_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF;
+                               } else {                                        /* An SFN entry is found */
+                                       if (ord != 0 || sum != sum_sfn(dp->dir)) {      /* Is there a valid LFN? */
+                                               dp->blk_ofs = 0xFFFFFFFF;                       /* It has no LFN. */
+                                       }
+                                       break;
+                               }
+                       }
+#else          /* Non LFN configuration */
+                       if (b != DDEM && b != '.' && attr != AM_LFN && (int)((attr & ~AM_ARC) == AM_VOL) == vol) {      /* Is it a valid entry? */
+                               break;
+                       }
+#endif
+               }
+               res = dir_next(dp, 0);          /* Next entry */
+               if (res != FR_OK) break;
+       }
+
+       if (res != FR_OK) dp->sect = 0;         /* Terminate the read operation on error or EOT */
+       return res;
+}
+
+#endif /* FF_FS_MINIMIZE <= 1 || FF_USE_LABEL || FF_FS_RPATH >= 2 */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Directory handling - Find an object in the directory                  */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT dir_find (      /* FR_OK(0):succeeded, !=0:error */
+       DIR* dp                                 /* Pointer to the directory object with the file name */
+)
+{
+       FRESULT res;
+       FATFS *fs = dp->obj.fs;
+       BYTE c;
+#if FF_USE_LFN
+       BYTE a, ord, sum;
+#endif
+
+       res = dir_sdi(dp, 0);                   /* Rewind directory object */
+       if (res != FR_OK) return res;
+#if FF_FS_EXFAT
+       if (fs->fs_type == FS_EXFAT) {  /* On the exFAT volume */
+               BYTE nc;
+               UINT di, ni;
+               WORD hash = xname_sum(fs->lfnbuf);              /* Hash value of the name to find */
+
+               while ((res = DIR_READ_FILE(dp)) == FR_OK) {    /* Read an item */
+#if FF_MAX_LFN < 255
+                       if (fs->dirbuf[XDIR_NumName] > FF_MAX_LFN) continue;                    /* Skip comparison if inaccessible object name */
+#endif
+                       if (ld_word(fs->dirbuf + XDIR_NameHash) != hash) continue;      /* Skip comparison if hash mismatched */
+                       for (nc = fs->dirbuf[XDIR_NumName], di = SZDIRE * 2, ni = 0; nc; nc--, di += 2, ni++) { /* Compare the name */
+                               if ((di % SZDIRE) == 0) di += 2;
+                               if (ff_wtoupper(ld_word(fs->dirbuf + di)) != ff_wtoupper(fs->lfnbuf[ni])) break;
+                       }
+                       if (nc == 0 && !fs->lfnbuf[ni]) break;  /* Name matched? */
+               }
+               return res;
+       }
+#endif
+       /* On the FAT/FAT32 volume */
+#if FF_USE_LFN
+       ord = sum = 0xFF; dp->blk_ofs = 0xFFFFFFFF;     /* Reset LFN sequence */
+#endif
+       do {
+               res = move_window(fs, dp->sect);
+               if (res != FR_OK) break;
+               c = dp->dir[DIR_Name];
+               if (c == 0) { res = FR_NO_FILE; break; }        /* Reached to end of table */
+#if FF_USE_LFN         /* LFN configuration */
+               dp->obj.attr = a = dp->dir[DIR_Attr] & AM_MASK;
+               if (c == DDEM || ((a & AM_VOL) && a != AM_LFN)) {       /* An entry without valid data */
+                       ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF;   /* Reset LFN sequence */
+               } else {
+                       if (a == AM_LFN) {                      /* An LFN entry is found */
+                               if (!(dp->fn[NSFLAG] & NS_NOLFN)) {
+                                       if (c & LLEF) {         /* Is it start of LFN sequence? */
+                                               sum = dp->dir[LDIR_Chksum];
+                                               c &= (BYTE)~LLEF; ord = c;      /* LFN start order */
+                                               dp->blk_ofs = dp->dptr; /* Start offset of LFN */
+                                       }
+                                       /* Check validity of the LFN entry and compare it with given name */
+                                       ord = (c == ord && sum == dp->dir[LDIR_Chksum] && cmp_lfn(fs->lfnbuf, dp->dir)) ? ord - 1 : 0xFF;
+                               }
+                       } else {                                        /* An SFN entry is found */
+                               if (ord == 0 && sum == sum_sfn(dp->dir)) break; /* LFN matched? */
+                               if (!(dp->fn[NSFLAG] & NS_LOSS) && !mem_cmp(dp->dir, dp->fn, 11)) break;        /* SFN matched? */
+                               ord = 0xFF; dp->blk_ofs = 0xFFFFFFFF;   /* Reset LFN sequence */
+                       }
+               }
+#else          /* Non LFN configuration */
+               dp->obj.attr = dp->dir[DIR_Attr] & AM_MASK;
+               if (!(dp->dir[DIR_Attr] & AM_VOL) && !mem_cmp(dp->dir, dp->fn, 11)) break;      /* Is it a valid entry? */
+#endif
+               res = dir_next(dp, 0);  /* Next entry */
+       } while (res == FR_OK);
+
+       return res;
+}
+
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Register an object to the directory                                   */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT dir_register (  /* FR_OK:succeeded, FR_DENIED:no free entry or too many SFN collision, FR_DISK_ERR:disk error */
+       DIR* dp                                         /* Target directory with object name to be created */
+)
+{
+       FRESULT res;
+       FATFS *fs = dp->obj.fs;
+#if FF_USE_LFN         /* LFN configuration */
+       UINT n, nlen, nent;
+       BYTE sn[12], sum;
+
+
+       if (dp->fn[NSFLAG] & (NS_DOT | NS_NONAME)) return FR_INVALID_NAME;      /* Check name validity */
+       for (nlen = 0; fs->lfnbuf[nlen]; nlen++) ;      /* Get lfn length */
+
+#if FF_FS_EXFAT
+       if (fs->fs_type == FS_EXFAT) {  /* On the exFAT volume */
+               nent = (nlen + 14) / 15 + 2;    /* Number of entries to allocate (85+C0+C1s) */
+               res = dir_alloc(dp, nent);              /* Allocate directory entries */
+               if (res != FR_OK) return res;
+               dp->blk_ofs = dp->dptr - SZDIRE * (nent - 1);   /* Set the allocated entry block offset */
+
+               if (dp->obj.stat & 4) {                 /* Has the directory been stretched by new allocation? */
+                       dp->obj.stat &= ~4;
+                       res = fill_first_frag(&dp->obj);        /* Fill the first fragment on the FAT if needed */
+                       if (res != FR_OK) return res;
+                       res = fill_last_frag(&dp->obj, dp->clust, 0xFFFFFFFF);  /* Fill the last fragment on the FAT if needed */
+                       if (res != FR_OK) return res;
+                       if (dp->obj.sclust != 0) {              /* Is it a sub-directory? */
+                               DIR dj;
+
+                               res = load_obj_xdir(&dj, &dp->obj);     /* Load the object status */
+                               if (res != FR_OK) return res;
+                               dp->obj.objsize += (DWORD)fs->csize * SS(fs);                   /* Increase the directory size by cluster size */
+                               st_qword(fs->dirbuf + XDIR_FileSize, dp->obj.objsize);
+                               st_qword(fs->dirbuf + XDIR_ValidFileSize, dp->obj.objsize);
+                               fs->dirbuf[XDIR_GenFlags] = dp->obj.stat | 1;                   /* Update the allocation status */
+                               res = store_xdir(&dj);                          /* Store the object status */
+                               if (res != FR_OK) return res;
+                       }
+               }
+
+               create_xdir(fs->dirbuf, fs->lfnbuf);    /* Create on-memory directory block to be written later */
+               return FR_OK;
+       }
+#endif
+       /* On the FAT/FAT32 volume */
+       mem_cpy(sn, dp->fn, 12);
+       if (sn[NSFLAG] & NS_LOSS) {                     /* When LFN is out of 8.3 format, generate a numbered name */
+               dp->fn[NSFLAG] = NS_NOLFN;              /* Find only SFN */
+               for (n = 1; n < 100; n++) {
+                       gen_numname(dp->fn, sn, fs->lfnbuf, n); /* Generate a numbered name */
+                       res = dir_find(dp);                             /* Check if the name collides with existing SFN */
+                       if (res != FR_OK) break;
+               }
+               if (n == 100) return FR_DENIED;         /* Abort if too many collisions */
+               if (res != FR_NO_FILE) return res;      /* Abort if the result is other than 'not collided' */
+               dp->fn[NSFLAG] = sn[NSFLAG];
+       }
+
+       /* Create an SFN with/without LFNs. */
+       nent = (sn[NSFLAG] & NS_LFN) ? (nlen + 12) / 13 + 1 : 1;        /* Number of entries to allocate */
+       res = dir_alloc(dp, nent);              /* Allocate entries */
+       if (res == FR_OK && --nent) {   /* Set LFN entry if needed */
+               res = dir_sdi(dp, dp->dptr - nent * SZDIRE);
+               if (res == FR_OK) {
+                       sum = sum_sfn(dp->fn);  /* Checksum value of the SFN tied to the LFN */
+                       do {                                    /* Store LFN entries in bottom first */
+                               res = move_window(fs, dp->sect);
+                               if (res != FR_OK) break;
+                               put_lfn(fs->lfnbuf, dp->dir, (BYTE)nent, sum);
+                               fs->wflag = 1;
+                               res = dir_next(dp, 0);  /* Next entry */
+                       } while (res == FR_OK && --nent);
+               }
+       }
+
+#else  /* Non LFN configuration */
+       res = dir_alloc(dp, 1);         /* Allocate an entry for SFN */
+
+#endif
+
+       /* Set SFN entry */
+       if (res == FR_OK) {
+               res = move_window(fs, dp->sect);
+               if (res == FR_OK) {
+                       mem_set(dp->dir, 0, SZDIRE);    /* Clean the entry */
+                       mem_cpy(dp->dir + DIR_Name, dp->fn, 11);        /* Put SFN */
+#if FF_USE_LFN
+                       dp->dir[DIR_NTres] = dp->fn[NSFLAG] & (NS_BODY | NS_EXT);       /* Put NT flag */
+#endif
+                       fs->wflag = 1;
+               }
+       }
+
+       return res;
+}
+
+#endif /* !FF_FS_READONLY */
+
+
+
+#if !FF_FS_READONLY && FF_FS_MINIMIZE == 0
+/*-----------------------------------------------------------------------*/
+/* Remove an object from the directory                                   */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT dir_remove (    /* FR_OK:Succeeded, FR_DISK_ERR:A disk error */
+       DIR* dp                                 /* Directory object pointing the entry to be removed */
+)
+{
+       FRESULT res;
+       FATFS *fs = dp->obj.fs;
+#if FF_USE_LFN         /* LFN configuration */
+       DWORD last = dp->dptr;
+
+       res = (dp->blk_ofs == 0xFFFFFFFF) ? FR_OK : dir_sdi(dp, dp->blk_ofs);   /* Goto top of the entry block if LFN is exist */
+       if (res == FR_OK) {
+               do {
+                       res = move_window(fs, dp->sect);
+                       if (res != FR_OK) break;
+                       if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) {   /* On the exFAT volume */
+                               dp->dir[XDIR_Type] &= 0x7F;     /* Clear the entry InUse flag. */
+                       } else {                                                                        /* On the FAT/FAT32 volume */
+                               dp->dir[DIR_Name] = DDEM;       /* Mark the entry 'deleted'. */
+                       }
+                       fs->wflag = 1;
+                       if (dp->dptr >= last) break;    /* If reached last entry then all entries of the object has been deleted. */
+                       res = dir_next(dp, 0);  /* Next entry */
+               } while (res == FR_OK);
+               if (res == FR_NO_FILE) res = FR_INT_ERR;
+       }
+#else                  /* Non LFN configuration */
+
+       res = move_window(fs, dp->sect);
+       if (res == FR_OK) {
+               dp->dir[DIR_Name] = DDEM;       /* Mark the entry 'deleted'.*/
+               fs->wflag = 1;
+       }
+#endif
+
+       return res;
+}
+
+#endif /* !FF_FS_READONLY && FF_FS_MINIMIZE == 0 */
+
+
+
+#if FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2
+/*-----------------------------------------------------------------------*/
+/* Get file information from directory entry                             */
+/*-----------------------------------------------------------------------*/
+
+static void get_fileinfo (
+       DIR* dp,                        /* Pointer to the directory object */
+       FILINFO* fno            /* Pointer to the file information to be filled */
+)
+{
+       UINT si, di;
+#if FF_USE_LFN
+       BYTE lcf;
+       WCHAR wc, hs;
+       FATFS *fs = dp->obj.fs;
+#else
+       TCHAR c;
+#endif
+
+
+       fno->fname[0] = 0;                      /* Invaidate file info */
+       if (dp->sect == 0) return;      /* Exit if read pointer has reached end of directory */
+
+#if FF_USE_LFN         /* LFN configuration */
+#if FF_FS_EXFAT
+       if (fs->fs_type == FS_EXFAT) {  /* On the exFAT volume */
+               get_xfileinfo(fs->dirbuf, fno);
+               return;
+       } else
+#endif
+       {       /* On the FAT/FAT32 volume */
+               if (dp->blk_ofs != 0xFFFFFFFF) {        /* Get LFN if available */
+                       si = di = hs = 0;
+                       while (fs->lfnbuf[si] != 0) {
+                               wc = fs->lfnbuf[si++];          /* Get an LFN character (UTF-16) */
+                               if (hs == 0 && IsSurrogate(wc)) {       /* Is it a surrogate? */
+                                       hs = wc; continue;              /* Get low surrogate */
+                               }
+                               wc = put_utf((DWORD)hs << 16 | wc, &fno->fname[di], FF_LFN_BUF - di);   /* Store it in UTF-16 or UTF-8 encoding */
+                               if (wc == 0) { di = 0; break; } /* Invalid char or buffer overflow? */
+                               di += wc;
+                               hs = 0;
+                       }
+                       if (hs != 0) di = 0;    /* Broken surrogate pair? */
+                       fno->fname[di] = 0;             /* Terminate the LFN (null string means LFN is invalid) */
+               }
+       }
+
+       si = di = 0;
+       while (si < 11) {               /* Get SFN from SFN entry */
+               wc = dp->dir[si++];                     /* Get a char */
+               if (wc == ' ') continue;        /* Skip padding spaces */
+               if (wc == RDDEM) wc = DDEM;     /* Restore replaced DDEM character */
+               if (si == 9 && di < FF_SFN_BUF) fno->altname[di++] = '.';       /* Insert a . if extension is exist */
+#if FF_LFN_UNICODE >= 1        /* Unicode output */
+               if (dbc_1st((BYTE)wc) && si != 8 && si != 11 && dbc_2nd(dp->dir[si])) { /* Make a DBC if needed */
+                       wc = wc << 8 | dp->dir[si++];
+               }
+               wc = ff_oem2uni(wc, CODEPAGE);          /* ANSI/OEM -> Unicode */
+               if (wc == 0) { di = 0; break; }         /* Wrong char in the current code page? */
+               wc = put_utf(wc, &fno->altname[di], FF_SFN_BUF - di);   /* Store it in Unicode */
+               if (wc == 0) { di = 0; break; }         /* Buffer overflow? */
+               di += wc;
+#else                                  /* ANSI/OEM output */
+               fno->altname[di++] = (TCHAR)wc; /* Store it without any conversion */
+#endif
+       }
+       fno->altname[di] = 0;   /* Terminate the SFN  (null string means SFN is invalid) */
+
+       if (fno->fname[0] == 0) {       /* If LFN is invalid, altname[] needs to be copied to fname[] */
+               if (di == 0) {  /* If LFN and SFN both are invalid, this object is inaccesible */
+                       fno->fname[di++] = '?';
+               } else {
+                       for (si = di = 0, lcf = NS_BODY; fno->altname[si]; si++, di++) {        /* Copy altname[] to fname[] with case information */
+                               wc = (WCHAR)fno->altname[si];
+                               if (wc == '.') lcf = NS_EXT;
+                               if (IsUpper(wc) && (dp->dir[DIR_NTres] & lcf)) wc += 0x20;
+                               fno->fname[di] = (TCHAR)wc;
+                       }
+               }
+               fno->fname[di] = 0;     /* Terminate the LFN */
+               if (!dp->dir[DIR_NTres]) fno->altname[0] = 0;   /* Altname is not needed if neither LFN nor case info is exist. */
+       }
+
+#else  /* Non-LFN configuration */
+       si = di = 0;
+       while (si < 11) {               /* Copy name body and extension */
+               c = (TCHAR)dp->dir[si++];
+               if (c == ' ') continue;         /* Skip padding spaces */
+               if (c == RDDEM) c = DDEM;       /* Restore replaced DDEM character */
+               if (si == 9) fno->fname[di++] = '.';/* Insert a . if extension is exist */
+               fno->fname[di++] = c;
+       }
+       fno->fname[di] = 0;
+#endif
+
+       fno->fattrib = dp->dir[DIR_Attr];                                       /* Attribute */
+       fno->fsize = ld_dword(dp->dir + DIR_FileSize);          /* Size */
+       fno->ftime = ld_word(dp->dir + DIR_ModTime + 0);        /* Time */
+       fno->fdate = ld_word(dp->dir + DIR_ModTime + 2);        /* Date */
+}
+
+#endif /* FF_FS_MINIMIZE <= 1 || FF_FS_RPATH >= 2 */
+
+
+
+#if FF_USE_FIND && FF_FS_MINIMIZE <= 1
+/*-----------------------------------------------------------------------*/
+/* Pattern matching                                                      */
+/*-----------------------------------------------------------------------*/
+
+static DWORD get_achar (       /* Get a character and advances ptr */
+       const TCHAR** ptr               /* Pointer to pointer to the ANSI/OEM or Unicode string */
+)
+{
+       DWORD chr;
+
+
+#if FF_USE_LFN && FF_LFN_UNICODE >= 1  /* Unicode input */
+       chr = tchar2uni(ptr);
+       if (chr == 0xFFFFFFFF) chr = 0;         /* Wrong UTF encoding is recognized as end of the string */
+       chr = ff_wtoupper(chr);
+
+#else                                                                  /* ANSI/OEM input */
+       chr = (BYTE)*(*ptr)++;                          /* Get a byte */
+       if (IsLower(chr)) chr -= 0x20;          /* To upper ASCII char */
+#if FF_CODE_PAGE == 0
+       if (ExCvt && chr >= 0x80) chr = ExCvt[chr - 0x80];      /* To upper SBCS extended char */
+#elif FF_CODE_PAGE < 900
+       if (chr >= 0x80) chr = ExCvt[chr - 0x80];       /* To upper SBCS extended char */
+#endif
+#if FF_CODE_PAGE == 0 || FF_CODE_PAGE >= 900
+       if (dbc_1st((BYTE)chr)) {       /* Get DBC 2nd byte if needed */
+               chr = dbc_2nd((BYTE)**ptr) ? chr << 8 | (BYTE)*(*ptr)++ : 0;
+       }
+#endif
+
+#endif
+       return chr;
+}
+
+
+static int pattern_matching (  /* 0:not matched, 1:matched */
+       const TCHAR* pat,       /* Matching pattern */
+       const TCHAR* nam,       /* String to be tested */
+       int skip,                       /* Number of pre-skip chars (number of ?s) */
+       int inf                         /* Infinite search (* specified) */
+)
+{
+       const TCHAR *pp, *np;
+       DWORD pc, nc;
+       int nm, nx;
+
+
+       while (skip--) {                                /* Pre-skip name chars */
+               if (!get_achar(&nam)) return 0; /* Branch mismatched if less name chars */
+       }
+       if (*pat == 0 && inf) return 1; /* (short circuit) */
+
+       do {
+               pp = pat; np = nam;                     /* Top of pattern and name to match */
+               for (;;) {
+                       if (*pp == '?' || *pp == '*') { /* Wildcard? */
+                               nm = nx = 0;
+                               do {                            /* Analyze the wildcard block */
+                                       if (*pp++ == '?') nm++; else nx = 1;
+                               } while (*pp == '?' || *pp == '*');
+                               if (pattern_matching(pp, np, nm, nx)) return 1; /* Test new branch (recurs upto number of wildcard blocks in the pattern) */
+                               nc = *np; break;        /* Branch mismatched */
+                       }
+                       pc = get_achar(&pp);    /* Get a pattern char */
+                       nc = get_achar(&np);    /* Get a name char */
+                       if (pc != nc) break;    /* Branch mismatched? */
+                       if (pc == 0) return 1;  /* Branch matched? (matched at end of both strings) */
+               }
+               get_achar(&nam);                        /* nam++ */
+       } while (inf && nc);                    /* Retry until end of name if infinite search is specified */
+
+       return 0;
+}
+
+#endif /* FF_USE_FIND && FF_FS_MINIMIZE <= 1 */
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Pick a top segment and create the object name in directory form       */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT create_name (   /* FR_OK: successful, FR_INVALID_NAME: could not create */
+       DIR* dp,                                        /* Pointer to the directory object */
+       const TCHAR** path                      /* Pointer to pointer to the segment in the path string */
+)
+{
+#if FF_USE_LFN         /* LFN configuration */
+       BYTE b, cf;
+       WCHAR wc, *lfn;
+       DWORD uc;
+       UINT i, ni, si, di;
+       const TCHAR *p;
+
+
+       /* Create LFN into LFN working buffer */
+       p = *path; lfn = dp->obj.fs->lfnbuf; di = 0;
+       for (;;) {
+               uc = tchar2uni(&p);                     /* Get a character */
+               if (uc == 0xFFFFFFFF) return FR_INVALID_NAME;           /* Invalid code or UTF decode error */
+               if (uc >= 0x10000) lfn[di++] = (WCHAR)(uc >> 16);       /* Store high surrogate if needed */
+               wc = (WCHAR)uc;
+               if (wc < ' ' || wc == '/' || wc == '\\') break; /* Break if end of the path or a separator is found */
+               if (wc < 0x80 && chk_chr("\"*:<>\?|\x7F", wc)) return FR_INVALID_NAME;  /* Reject illegal characters for LFN */
+               if (di >= FF_MAX_LFN) return FR_INVALID_NAME;   /* Reject too long name */
+               lfn[di++] = wc;                                 /* Store the Unicode character */
+       }
+       if (wc < ' ') {                         /* End of path? */
+               cf = NS_LAST;                   /* Set last segment flag */
+       } else {
+               cf = 0;                                 /* Next segment follows */
+               while (*p == '/' || *p == '\\') p++;    /* Skip duplicated separators if exist */
+       }
+       *path = p;                                      /* Return pointer to the next segment */
+
+#if FF_FS_RPATH != 0
+       if ((di == 1 && lfn[di - 1] == '.') ||
+               (di == 2 && lfn[di - 1] == '.' && lfn[di - 2] == '.')) {        /* Is this segment a dot name? */
+               lfn[di] = 0;
+               for (i = 0; i < 11; i++) {              /* Create dot name for SFN entry */
+                       dp->fn[i] = (i < di) ? '.' : ' ';
+               }
+               dp->fn[i] = cf | NS_DOT;                /* This is a dot entry */
+               return FR_OK;
+       }
+#endif
+       while (di) {                                            /* Snip off trailing spaces and dots if exist */
+               wc = lfn[di - 1];
+               if (wc != ' ' && wc != '.') break;
+               di--;
+       }
+       lfn[di] = 0;                                                    /* LFN is created into the working buffer */
+       if (di == 0) return FR_INVALID_NAME;    /* Reject null name */
+
+       /* Create SFN in directory form */
+       for (si = 0; lfn[si] == ' '; si++) ;    /* Remove leading spaces */
+       if (si > 0 || lfn[si] == '.') cf |= NS_LOSS | NS_LFN;   /* Is there any leading space or dot? */
+       while (di > 0 && lfn[di - 1] != '.') di--;      /* Find last dot (di<=si: no extension) */
+
+       mem_set(dp->fn, ' ', 11);
+       i = b = 0; ni = 8;
+       for (;;) {
+               wc = lfn[si++];                                 /* Get an LFN character */
+               if (wc == 0) break;                             /* Break on end of the LFN */
+               if (wc == ' ' || (wc == '.' && si != di)) {     /* Remove embedded spaces and dots */
+                       cf |= NS_LOSS | NS_LFN;
+                       continue;
+               }
+
+               if (i >= ni || si == di) {              /* End of field? */
+                       if (ni == 11) {                         /* Name extension overflow? */
+                               cf |= NS_LOSS | NS_LFN;
+                               break;
+                       }
+                       if (si != di) cf |= NS_LOSS | NS_LFN;   /* Name body overflow? */
+                       if (si > di) break;                                             /* No name extension? */
+                       si = di; i = 8; ni = 11; b <<= 2;               /* Enter name extension */
+                       continue;
+               }
+
+               if (wc >= 0x80) {       /* Is this a non-ASCII character? */
+                       cf |= NS_LFN;   /* LFN entry needs to be created */
+#if FF_CODE_PAGE == 0
+                       if (ExCvt) {    /* At SBCS */
+                               wc = ff_uni2oem(wc, CODEPAGE);                  /* Unicode ==> ANSI/OEM code */
+                               if (wc & 0x80) wc = ExCvt[wc & 0x7F];   /* Convert extended character to upper (SBCS) */
+                       } else {                /* At DBCS */
+                               wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE);     /* Unicode ==> Upper convert ==> ANSI/OEM code */
+                       }
+#elif FF_CODE_PAGE < 900       /* SBCS cfg */
+                       wc = ff_uni2oem(wc, CODEPAGE);                  /* Unicode ==> ANSI/OEM code */
+                       if (wc & 0x80) wc = ExCvt[wc & 0x7F];   /* Convert extended character to upper (SBCS) */
+#else                                          /* DBCS cfg */
+                       wc = ff_uni2oem(ff_wtoupper(wc), CODEPAGE);     /* Unicode ==> Upper convert ==> ANSI/OEM code */
+#endif
+               }
+
+               if (wc >= 0x100) {                              /* Is this a DBC? */
+                       if (i >= ni - 1) {                      /* Field overflow? */
+                               cf |= NS_LOSS | NS_LFN;
+                               i = ni; continue;               /* Next field */
+                       }
+                       dp->fn[i++] = (BYTE)(wc >> 8);  /* Put 1st byte */
+               } else {                                                /* SBC */
+                       if (wc == 0 || chk_chr("+,;=[]", wc)) { /* Replace illegal characters for SFN if needed */
+                               wc = '_'; cf |= NS_LOSS | NS_LFN;/* Lossy conversion */
+                       } else {
+                               if (IsUpper(wc)) {              /* ASCII upper case? */
+                                       b |= 2;
+                               }
+                               if (IsLower(wc)) {              /* ASCII lower case? */
+                                       b |= 1; wc -= 0x20;
+                               }
+                       }
+               }
+               dp->fn[i++] = (BYTE)wc;
+       }
+
+       if (dp->fn[0] == DDEM) dp->fn[0] = RDDEM;       /* If the first character collides with DDEM, replace it with RDDEM */
+
+       if (ni == 8) b <<= 2;                           /* Shift capital flags if no extension */
+       if ((b & 0x0C) == 0x0C || (b & 0x03) == 0x03) cf |= NS_LFN;     /* LFN entry needs to be created if composite capitals */
+       if (!(cf & NS_LFN)) {                           /* When LFN is in 8.3 format without extended character, NT flags are created */
+               if (b & 0x01) cf |= NS_EXT;             /* NT flag (Extension has small capital letters only) */
+               if (b & 0x04) cf |= NS_BODY;    /* NT flag (Body has small capital letters only) */
+       }
+
+       dp->fn[NSFLAG] = cf;    /* SFN is created into dp->fn[] */
+
+       return FR_OK;
+
+
+#else  /* FF_USE_LFN : Non-LFN configuration */
+       BYTE c, d, *sfn;
+       UINT ni, si, i;
+       const char *p;
+
+       /* Create file name in directory form */
+       p = *path; sfn = dp->fn;
+       mem_set(sfn, ' ', 11);
+       si = i = 0; ni = 8;
+#if FF_FS_RPATH != 0
+       if (p[si] == '.') { /* Is this a dot entry? */
+               for (;;) {
+                       c = (BYTE)p[si++];
+                       if (c != '.' || si >= 3) break;
+                       sfn[i++] = c;
+               }
+               if (c != '/' && c != '\\' && c > ' ') return FR_INVALID_NAME;
+               *path = p + si;                                                         /* Return pointer to the next segment */
+               sfn[NSFLAG] = (c <= ' ') ? NS_LAST | NS_DOT : NS_DOT;   /* Set last segment flag if end of the path */
+               return FR_OK;
+       }
+#endif
+       for (;;) {
+               c = (BYTE)p[si++];                              /* Get a byte */
+               if (c <= ' ') break;                    /* Break if end of the path name */
+               if (c == '/' || c == '\\') {    /* Break if a separator is found */
+                       while (p[si] == '/' || p[si] == '\\') si++;     /* Skip duplicated separator if exist */
+                       break;
+               }
+               if (c == '.' || i >= ni) {              /* End of body or field overflow? */
+                       if (ni == 11 || c != '.') return FR_INVALID_NAME;       /* Field overflow or invalid dot? */
+                       i = 8; ni = 11;                         /* Enter file extension field */
+                       continue;
+               }
+#if FF_CODE_PAGE == 0
+               if (ExCvt && c >= 0x80) {               /* Is SBC extended character? */
+                       c = ExCvt[c & 0x7F];            /* To upper SBC extended character */
+               }
+#elif FF_CODE_PAGE < 900
+               if (c >= 0x80) {                                /* Is SBC extended character? */
+                       c = ExCvt[c & 0x7F];            /* To upper SBC extended character */
+               }
+#endif
+               if (dbc_1st(c)) {                               /* Check if it is a DBC 1st byte */
+                       d = (BYTE)p[si++];                      /* Get 2nd byte */
+                       if (!dbc_2nd(d) || i >= ni - 1) return FR_INVALID_NAME; /* Reject invalid DBC */
+                       sfn[i++] = c;
+                       sfn[i++] = d;
+               } else {                                                /* SBC */
+                       if (chk_chr("\"*+,:;<=>\?[]|\x7F", c)) return FR_INVALID_NAME;  /* Reject illegal chrs for SFN */
+                       if (IsLower(c)) c -= 0x20;      /* To upper */
+                       sfn[i++] = c;
+               }
+       }
+       *path = p + si;                                         /* Return pointer to the next segment */
+       if (i == 0) return FR_INVALID_NAME;     /* Reject nul string */
+
+       if (sfn[0] == DDEM) sfn[0] = RDDEM;     /* If the first character collides with DDEM, replace it with RDDEM */
+       sfn[NSFLAG] = (c <= ' ') ? NS_LAST : 0;         /* Set last segment flag if end of the path */
+
+       return FR_OK;
+#endif /* FF_USE_LFN */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Follow a file path                                                    */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT follow_path (   /* FR_OK(0): successful, !=0: error code */
+       DIR* dp,                                        /* Directory object to return last directory and found object */
+       const TCHAR* path                       /* Full-path string to find a file or directory */
+)
+{
+       FRESULT res;
+       BYTE ns;
+       FATFS *fs = dp->obj.fs;
+
+
+#if FF_FS_RPATH != 0
+       if (*path != '/' && *path != '\\') {    /* Without heading separator */
+               dp->obj.sclust = fs->cdir;                              /* Start from current directory */
+       } else
+#endif
+       {                                                                               /* With heading separator */
+               while (*path == '/' || *path == '\\') path++;   /* Strip heading separator */
+               dp->obj.sclust = 0;                                     /* Start from root directory */
+       }
+#if FF_FS_EXFAT
+       dp->obj.n_frag = 0;     /* Invalidate last fragment counter of the object */
+#if FF_FS_RPATH != 0
+       if (fs->fs_type == FS_EXFAT && dp->obj.sclust) {        /* exFAT: Retrieve the sub-directory's status */
+               DIR dj;
+
+               dp->obj.c_scl = fs->cdc_scl;
+               dp->obj.c_size = fs->cdc_size;
+               dp->obj.c_ofs = fs->cdc_ofs;
+               res = load_obj_xdir(&dj, &dp->obj);
+               if (res != FR_OK) return res;
+               dp->obj.objsize = ld_dword(fs->dirbuf + XDIR_FileSize);
+               dp->obj.stat = fs->dirbuf[XDIR_GenFlags] & 2;
+       }
+#endif
+#endif
+
+       if ((UINT)*path < ' ') {                                /* Null path name is the origin directory itself */
+               dp->fn[NSFLAG] = NS_NONAME;
+               res = dir_sdi(dp, 0);
+
+       } else {                                                                /* Follow path */
+               for (;;) {
+                       res = create_name(dp, &path);   /* Get a segment name of the path */
+                       if (res != FR_OK) break;
+                       res = dir_find(dp);                             /* Find an object with the segment name */
+                       ns = dp->fn[NSFLAG];
+                       if (res != FR_OK) {                             /* Failed to find the object */
+                               if (res == FR_NO_FILE) {        /* Object is not found */
+                                       if (FF_FS_RPATH && (ns & NS_DOT)) {     /* If dot entry is not exist, stay there */
+                                               if (!(ns & NS_LAST)) continue;  /* Continue to follow if not last segment */
+                                               dp->fn[NSFLAG] = NS_NONAME;
+                                               res = FR_OK;
+                                       } else {                                                        /* Could not find the object */
+                                               if (!(ns & NS_LAST)) res = FR_NO_PATH;  /* Adjust error code if not last segment */
+                                       }
+                               }
+                               break;
+                       }
+                       if (ns & NS_LAST) break;                        /* Last segment matched. Function completed. */
+                       /* Get into the sub-directory */
+                       if (!(dp->obj.attr & AM_DIR)) {         /* It is not a sub-directory and cannot follow */
+                               res = FR_NO_PATH; break;
+                       }
+#if FF_FS_EXFAT
+                       if (fs->fs_type == FS_EXFAT) {          /* Save containing directory information for next dir */
+                               dp->obj.c_scl = dp->obj.sclust;
+                               dp->obj.c_size = ((DWORD)dp->obj.objsize & 0xFFFFFF00) | dp->obj.stat;
+                               dp->obj.c_ofs = dp->blk_ofs;
+                               init_alloc_info(fs, &dp->obj);  /* Open next directory */
+                       } else
+#endif
+                       {
+                               dp->obj.sclust = ld_clust(fs, fs->win + dp->dptr % SS(fs));     /* Open next directory */
+                       }
+               }
+       }
+
+       return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Get logical drive number from path name                               */
+/*-----------------------------------------------------------------------*/
+
+static int get_ldnumber (      /* Returns logical drive number (-1:invalid drive number or null pointer) */
+       const TCHAR** path              /* Pointer to pointer to the path name */
+)
+{
+       const TCHAR *tp, *tt;
+       TCHAR tc;
+       int i, vol = -1;
+#if FF_STR_VOLUME_ID           /* Find string volume ID */
+       const char *sp;
+       char c;
+#endif
+
+       tt = tp = *path;
+       if (!tp) return vol;    /* Invalid path name? */
+       do tc = *tt++; while ((UINT)tc >= (FF_USE_LFN ? ' ' : '!') && tc != ':');       /* Find a colon in the path */
+
+       if (tc == ':') {        /* DOS/Windows style volume ID? */
+               i = FF_VOLUMES;
+               if (IsDigit(*tp) && tp + 2 == tt) {     /* Is there a numeric volume ID + colon? */
+                       i = (int)*tp - '0';     /* Get the LD number */
+               }
+#if FF_STR_VOLUME_ID == 1      /* Arbitrary string is enabled */
+               else {
+                       i = 0;
+                       do {
+                               sp = VolumeStr[i]; tp = *path;  /* This string volume ID and path name */
+                               do {    /* Compare the volume ID with path name */
+                                       c = *sp++; tc = *tp++;
+                                       if (IsLower(c)) c -= 0x20;
+                                       if (IsLower(tc)) tc -= 0x20;
+                               } while (c && (TCHAR)c == tc);
+                       } while ((c || tp != tt) && ++i < FF_VOLUMES);  /* Repeat for each id until pattern match */
+               }
+#endif
+               if (i < FF_VOLUMES) {   /* If a volume ID is found, get the drive number and strip it */
+                       vol = i;                /* Drive number */
+                       *path = tt;             /* Snip the drive prefix off */
+               }
+               return vol;
+       }
+#if FF_STR_VOLUME_ID == 2              /* Unix style volume ID is enabled */
+       if (*tp == '/') {
+               i = 0;
+               do {
+                       sp = VolumeStr[i]; tp = *path;  /* This string volume ID and path name */
+                       do {    /* Compare the volume ID with path name */
+                               c = *sp++; tc = *(++tp);
+                               if (IsLower(c)) c -= 0x20;
+                               if (IsLower(tc)) tc -= 0x20;
+                       } while (c && (TCHAR)c == tc);
+               } while ((c || (tc != '/' && (UINT)tc >= (FF_USE_LFN ? ' ' : '!'))) && ++i < FF_VOLUMES);       /* Repeat for each ID until pattern match */
+               if (i < FF_VOLUMES) {   /* If a volume ID is found, get the drive number and strip it */
+                       vol = i;                /* Drive number */
+                       *path = tp;             /* Snip the drive prefix off */
+                       return vol;
+               }
+       }
+#endif
+       /* No drive prefix is found */
+#if FF_FS_RPATH != 0
+       vol = CurrVol;  /* Default drive is current drive */
+#else
+       vol = 0;                /* Default drive is 0 */
+#endif
+       return vol;             /* Return the default drive */
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* GPT support functions                                                 */
+/*-----------------------------------------------------------------------*/
+
+#if FF_LBA64
+
+/* Calculate CRC32 in byte-by-byte */
+
+static DWORD crc32 (   /* Returns next CRC value */
+       DWORD crc,                      /* Current CRC value */
+       BYTE d                          /* A byte to be processed */
+)
+{
+       BYTE b;
+
+
+       for (b = 1; b; b <<= 1) {
+               crc ^= (d & b) ? 1 : 0;
+               crc = (crc & 1) ? crc >> 1 ^ 0xEDB88320 : crc >> 1;
+       }
+       return crc;
+}
+
+
+/* Check validity of GPT header */
+
+static int test_gpt_header (   /* 0:Invalid, 1:Valid */
+       const BYTE* gpth                        /* Pointer to the GPT header */
+)
+{
+       UINT i;
+       DWORD bcc;
+
+
+       if (mem_cmp(gpth + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", 16)) return 0;  /* Check sign, version (1.0) and length (92) */
+       for (i = 0, bcc = 0xFFFFFFFF; i < 92; i++) {            /* Check header BCC */
+               bcc = crc32(bcc, i - GPTH_Bcc < 4 ? 0 : gpth[i]);
+       }
+       if (~bcc != ld_dword(gpth + GPTH_Bcc)) return 0;
+       if (ld_dword(gpth + GPTH_PteSize) != SZ_GPTE) return 0; /* Table entry size (must be SZ_GPTE bytes) */
+       if (ld_dword(gpth + GPTH_PtNum) > 128) return 0;        /* Table size (must be 128 entries or less) */
+
+       return 1;
+}
+
+#if !FF_FS_READONLY && FF_USE_MKFS
+
+/* Generate random value */
+static DWORD make_rand (
+       DWORD seed,             /* Seed value */
+       BYTE* buff,             /* Output buffer */
+       UINT n                  /* Data length */
+)
+{
+       UINT r;
+
+
+       if (seed == 0) seed = 1;
+       do {
+               for (r = 0; r < 8; r++) seed = seed & 1 ? seed >> 1 ^ 0xA3000000 : seed >> 1;   /* Shift 8 bits the 32-bit LFSR */
+               *buff++ = (BYTE)seed;
+       } while (--n);
+       return seed;
+}
+
+#endif
+#endif
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Load a sector and check if it is an FAT VBR                           */
+/*-----------------------------------------------------------------------*/
+
+/* Check what the sector is */
+
+static UINT check_fs ( /* 0:FAT VBR, 1:exFAT VBR, 2:Valid BS but not FAT, 3:Invalid BS, 4:Disk error */
+       FATFS* fs,                      /* Filesystem object */
+       LBA_t sect                      /* Sector to load and check if it is an FAT-VBR or not */
+)
+{
+       fs->wflag = 0; fs->winsect = (LBA_t)0 - 1;              /* Invaidate window */
+       if (move_window(fs, sect) != FR_OK) return 4;   /* Load the boot sector */
+
+       if (ld_word(fs->win + BS_55AA) != 0xAA55) return 3;     /* Check boot signature (always here regardless of the sector size) */
+
+       if (FF_FS_EXFAT && !mem_cmp(fs->win + BS_JmpBoot, "\xEB\x76\x90" "EXFAT   ", 11)) return 1;     /* Check if exFAT VBR */
+
+       if (fs->win[BS_JmpBoot] == 0xE9 || fs->win[BS_JmpBoot] == 0xEB || fs->win[BS_JmpBoot] == 0xE8) {        /* Valid JumpBoot code? */
+               if (!mem_cmp(fs->win + BS_FilSysType, "FAT", 3)) return 0;              /* Is it an FAT VBR? */
+               if (!mem_cmp(fs->win + BS_FilSysType32, "FAT32", 5)) return 0;  /* Is it an FAT32 VBR? */
+       }
+       return 2;       /* Valid BS but not FAT */
+}
+
+
+/* Find an FAT volume */
+/* (It supports only generic partitioning rules, MBR, GPT and SFD) */
+
+static UINT find_volume (      /* Returns BS status found in the hosting drive */
+       FATFS* fs,              /* Filesystem object */
+       UINT part               /* Partition to fined = 0:auto, 1..:forced */
+)
+{
+       UINT fmt, i;
+       DWORD mbr_pt[4];
+
+
+       fmt = check_fs(fs, 0);                          /* Load sector 0 and check if it is an FAT VBR as SFD */
+       if (fmt != 2 && (fmt >= 3 || part == 0)) return fmt;    /* Returns if it is a FAT VBR as auto scan, not a BS or disk error */
+
+       /* Sector 0 is not an FAT VBR or forced partition number wants a partition */
+
+#if FF_LBA64
+       if (fs->win[MBR_Table + PTE_System] == 0xEE) {  /* GPT protective MBR? */
+               DWORD n_ent, v_ent, ofs;
+               QWORD pt_lba;
+
+               if (move_window(fs, 1) != FR_OK) return 4;      /* Load GPT header sector (next to MBR) */
+               if (!test_gpt_header(fs->win)) return 3;        /* Check if GPT header is valid */
+               n_ent = ld_dword(fs->win + GPTH_PtNum);         /* Number of entries */
+               pt_lba = ld_qword(fs->win + GPTH_PtOfs);        /* Table location */
+               for (v_ent = i = 0; i < n_ent; i++) {           /* Find FAT partition */
+                       if (move_window(fs, pt_lba + i * SZ_GPTE / SS(fs)) != FR_OK) return 4;  /* PT sector */
+                       ofs = i * SZ_GPTE % SS(fs);                                                                                             /* Offset in the sector */
+                       if (!mem_cmp(fs->win + ofs + GPTE_PtGuid, GUID_MS_Basic, 16)) { /* MS basic data partition? */
+                               v_ent++;
+                               fmt = check_fs(fs, ld_qword(fs->win + ofs + GPTE_FstLba));      /* Load VBR and check status */
+                               if (part == 0 && fmt <= 1) return fmt;                  /* Auto search (valid FAT volume found first) */
+                               if (part != 0 && v_ent == part) return fmt;             /* Forced partition order (regardless of it is valid or not) */
+                       }
+               }
+               return 3;       /* Not found */
+       }
+#endif
+       if (FF_MULTI_PARTITION && part > 4) return 3;   /* MBR has 4 partitions max */
+       for (i = 0; i < 4; i++) {               /* Load partition offset in the MBR */
+               mbr_pt[i] = ld_dword(fs->win + MBR_Table + i * SZ_PTE + PTE_StLba);
+       }
+       i = part ? part - 1 : 0;                /* Table index to find first */
+       do {                                                    /* Find an FAT volume */
+               fmt = mbr_pt[i] ? check_fs(fs, mbr_pt[i]) : 3;  /* Check if the partition is FAT */
+       } while (part == 0 && fmt >= 2 && ++i < 4);
+       return fmt;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Determine logical drive number and mount the volume if needed         */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT mount_volume (  /* FR_OK(0): successful, !=0: an error occurred */
+       const TCHAR** path,                     /* Pointer to pointer to the path name (drive number) */
+       FATFS** rfs,                            /* Pointer to pointer to the found filesystem object */
+       BYTE mode                                       /* !=0: Check write protection for write access */
+)
+{
+       int vol;
+       DSTATUS stat;
+       LBA_t bsect;
+       DWORD tsect, sysect, fasize, nclst, szbfat;
+       WORD nrsv;
+       FATFS *fs;
+       UINT fmt;
+
+
+       /* Get logical drive number */
+       *rfs = 0;
+       vol = get_ldnumber(path);
+       if (vol < 0) return FR_INVALID_DRIVE;
+
+       /* Check if the filesystem object is valid or not */
+       fs = FatFs[vol];                                        /* Get pointer to the filesystem object */
+       if (!fs) return FR_NOT_ENABLED;         /* Is the filesystem object available? */
+#if FF_FS_REENTRANT
+       if (!lock_fs(fs)) return FR_TIMEOUT;    /* Lock the volume */
+#endif
+       *rfs = fs;                                                      /* Return pointer to the filesystem object */
+
+       mode &= (BYTE)~FA_READ;                         /* Desired access mode, write access or not */
+       if (fs->fs_type != 0) {                         /* If the volume has been mounted */
+               stat = disk_status(fs->pdrv);
+               if (!(stat & STA_NOINIT)) {             /* and the physical drive is kept initialized */
+                       if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) {  /* Check write protection if needed */
+                               return FR_WRITE_PROTECTED;
+                       }
+                       return FR_OK;                           /* The filesystem object is already valid */
+               }
+       }
+
+       /* The filesystem object is not valid. */
+       /* Following code attempts to mount the volume. (find a FAT volume, analyze the BPB and initialize the filesystem object) */
+
+       fs->fs_type = 0;                                        /* Clear the filesystem object */
+       fs->pdrv = LD2PD(vol);                          /* Volume hosting physical drive */
+       stat = disk_initialize(fs->pdrv);       /* Initialize the physical drive */
+       if (stat & STA_NOINIT) {                        /* Check if the initialization succeeded */
+               return FR_NOT_READY;                    /* Failed to initialize due to no medium or hard error */
+       }
+       if (!FF_FS_READONLY && mode && (stat & STA_PROTECT)) { /* Check disk write protection if needed */
+               return FR_WRITE_PROTECTED;
+       }
+#if FF_MAX_SS != FF_MIN_SS                             /* Get sector size (multiple sector size cfg only) */
+       if (disk_ioctl(fs->pdrv, GET_SECTOR_SIZE, &SS(fs)) != RES_OK) return FR_DISK_ERR;
+       if (SS(fs) > FF_MAX_SS || SS(fs) < FF_MIN_SS || (SS(fs) & (SS(fs) - 1))) return FR_DISK_ERR;
+#endif
+
+       /* Find an FAT volume on the drive */
+       fmt = find_volume(fs, LD2PT(vol));
+       if (fmt == 4) return FR_DISK_ERR;               /* An error occured in the disk I/O layer */
+       if (fmt >= 2) return FR_NO_FILESYSTEM;  /* No FAT volume is found */
+       bsect = fs->winsect;                                    /* Volume location */
+
+       /* An FAT volume is found (bsect). Following code initializes the filesystem object */
+
+#if FF_FS_EXFAT
+       if (fmt == 1) {
+               QWORD maxlba;
+               DWORD so, cv, bcl, i;
+
+               for (i = BPB_ZeroedEx; i < BPB_ZeroedEx + 53 && fs->win[i] == 0; i++) ; /* Check zero filler */
+               if (i < BPB_ZeroedEx + 53) return FR_NO_FILESYSTEM;
+
+               if (ld_word(fs->win + BPB_FSVerEx) != 0x100) return FR_NO_FILESYSTEM;   /* Check exFAT version (must be version 1.0) */
+
+               if (1 << fs->win[BPB_BytsPerSecEx] != SS(fs)) { /* (BPB_BytsPerSecEx must be equal to the physical sector size) */
+                       return FR_NO_FILESYSTEM;
+               }
+
+               maxlba = ld_qword(fs->win + BPB_TotSecEx) + bsect;      /* Last LBA + 1 of the volume */
+               if (!FF_LBA64 && maxlba >= 0x100000000) return FR_NO_FILESYSTEM;        /* (It cannot be handled in 32-bit LBA) */
+
+               fs->fsize = ld_dword(fs->win + BPB_FatSzEx);    /* Number of sectors per FAT */
+
+               fs->n_fats = fs->win[BPB_NumFATsEx];                    /* Number of FATs */
+               if (fs->n_fats != 1) return FR_NO_FILESYSTEM;   /* (Supports only 1 FAT) */
+
+               fs->csize = 1 << fs->win[BPB_SecPerClusEx];             /* Cluster size */
+               if (fs->csize == 0)     return FR_NO_FILESYSTEM;        /* (Must be 1..32768) */
+
+               nclst = ld_dword(fs->win + BPB_NumClusEx);              /* Number of clusters */
+               if (nclst > MAX_EXFAT) return FR_NO_FILESYSTEM; /* (Too many clusters) */
+               fs->n_fatent = nclst + 2;
+
+               /* Boundaries and Limits */
+               fs->volbase = bsect;
+               fs->database = bsect + ld_dword(fs->win + BPB_DataOfsEx);
+               fs->fatbase = bsect + ld_dword(fs->win + BPB_FatOfsEx);
+               if (maxlba < (QWORD)fs->database + nclst * fs->csize) return FR_NO_FILESYSTEM;  /* (Volume size must not be smaller than the size requiered) */
+               fs->dirbase = ld_dword(fs->win + BPB_RootClusEx);
+
+               /* Get bitmap location and check if it is contiguous (implementation assumption) */
+               so = i = 0;
+               for (;;) {      /* Find the bitmap entry in the root directory (in only first cluster) */
+                       if (i == 0) {
+                               if (so >= fs->csize) return FR_NO_FILESYSTEM;   /* Not found? */
+                               if (move_window(fs, clst2sect(fs, (DWORD)fs->dirbase) + so) != FR_OK) return FR_DISK_ERR;
+                               so++;
+                       }
+                       if (fs->win[i] == ET_BITMAP) break;                             /* Is it a bitmap entry? */
+                       i = (i + SZDIRE) % SS(fs);      /* Next entry */
+               }
+               bcl = ld_dword(fs->win + i + 20);                                       /* Bitmap cluster */
+               if (bcl < 2 || bcl >= fs->n_fatent) return FR_NO_FILESYSTEM;
+               fs->bitbase = fs->database + fs->csize * (bcl - 2);     /* Bitmap sector */
+               for (;;) {      /* Check if bitmap is contiguous */
+                       if (move_window(fs, fs->fatbase + bcl / (SS(fs) / 4)) != FR_OK) return FR_DISK_ERR;
+                       cv = ld_dword(fs->win + bcl % (SS(fs) / 4) * 4);
+                       if (cv == 0xFFFFFFFF) break;                            /* Last link? */
+                       if (cv != ++bcl) return FR_NO_FILESYSTEM;       /* Fragmented? */
+               }
+
+#if !FF_FS_READONLY
+               fs->last_clst = fs->free_clst = 0xFFFFFFFF;             /* Initialize cluster allocation information */
+#endif
+               fmt = FS_EXFAT;                 /* FAT sub-type */
+       } else
+#endif /* FF_FS_EXFAT */
+       {
+               if (ld_word(fs->win + BPB_BytsPerSec) != SS(fs)) return FR_NO_FILESYSTEM;       /* (BPB_BytsPerSec must be equal to the physical sector size) */
+
+               fasize = ld_word(fs->win + BPB_FATSz16);                /* Number of sectors per FAT */
+               if (fasize == 0) fasize = ld_dword(fs->win + BPB_FATSz32);
+               fs->fsize = fasize;
+
+               fs->n_fats = fs->win[BPB_NumFATs];                              /* Number of FATs */
+               if (fs->n_fats != 1 && fs->n_fats != 2) return FR_NO_FILESYSTEM;        /* (Must be 1 or 2) */
+               fasize *= fs->n_fats;                                                   /* Number of sectors for FAT area */
+
+               fs->csize = fs->win[BPB_SecPerClus];                    /* Cluster size */
+               if (fs->csize == 0 || (fs->csize & (fs->csize - 1))) return FR_NO_FILESYSTEM;   /* (Must be power of 2) */
+
+               fs->n_rootdir = ld_word(fs->win + BPB_RootEntCnt);      /* Number of root directory entries */
+               if (fs->n_rootdir % (SS(fs) / SZDIRE)) return FR_NO_FILESYSTEM; /* (Must be sector aligned) */
+
+               tsect = ld_word(fs->win + BPB_TotSec16);                /* Number of sectors on the volume */
+               if (tsect == 0) tsect = ld_dword(fs->win + BPB_TotSec32);
+
+               nrsv = ld_word(fs->win + BPB_RsvdSecCnt);               /* Number of reserved sectors */
+               if (nrsv == 0) return FR_NO_FILESYSTEM;                 /* (Must not be 0) */
+
+               /* Determine the FAT sub type */
+               sysect = nrsv + fasize + fs->n_rootdir / (SS(fs) / SZDIRE);     /* RSV + FAT + DIR */
+               if (tsect < sysect) return FR_NO_FILESYSTEM;    /* (Invalid volume size) */
+               nclst = (tsect - sysect) / fs->csize;                   /* Number of clusters */
+               if (nclst == 0) return FR_NO_FILESYSTEM;                /* (Invalid volume size) */
+               fmt = 0;
+               if (nclst <= MAX_FAT32) fmt = FS_FAT32;
+               if (nclst <= MAX_FAT16) fmt = FS_FAT16;
+               if (nclst <= MAX_FAT12) fmt = FS_FAT12;
+               if (fmt == 0) return FR_NO_FILESYSTEM;
+
+               /* Boundaries and Limits */
+               fs->n_fatent = nclst + 2;                                               /* Number of FAT entries */
+               fs->volbase = bsect;                                                    /* Volume start sector */
+               fs->fatbase = bsect + nrsv;                                     /* FAT start sector */
+               fs->database = bsect + sysect;                                  /* Data start sector */
+               if (fmt == FS_FAT32) {
+                       if (ld_word(fs->win + BPB_FSVer32) != 0) return FR_NO_FILESYSTEM;       /* (Must be FAT32 revision 0.0) */
+                       if (fs->n_rootdir != 0) return FR_NO_FILESYSTEM;        /* (BPB_RootEntCnt must be 0) */
+                       fs->dirbase = ld_dword(fs->win + BPB_RootClus32);       /* Root directory start cluster */
+                       szbfat = fs->n_fatent * 4;                                      /* (Needed FAT size) */
+               } else {
+                       if (fs->n_rootdir == 0) return FR_NO_FILESYSTEM;        /* (BPB_RootEntCnt must not be 0) */
+                       fs->dirbase = fs->fatbase + fasize;                     /* Root directory start sector */
+                       szbfat = (fmt == FS_FAT16) ?                            /* (Needed FAT size) */
+                               fs->n_fatent * 2 : fs->n_fatent * 3 / 2 + (fs->n_fatent & 1);
+               }
+               if (fs->fsize < (szbfat + (SS(fs) - 1)) / SS(fs)) return FR_NO_FILESYSTEM;      /* (BPB_FATSz must not be less than the size needed) */
+
+#if !FF_FS_READONLY
+               /* Get FSInfo if available */
+               fs->last_clst = fs->free_clst = 0xFFFFFFFF;             /* Initialize cluster allocation information */
+               fs->fsi_flag = 0x80;
+#if (FF_FS_NOFSINFO & 3) != 3
+               if (fmt == FS_FAT32                             /* Allow to update FSInfo only if BPB_FSInfo32 == 1 */
+                       && ld_word(fs->win + BPB_FSInfo32) == 1
+                       && move_window(fs, bsect + 1) == FR_OK)
+               {
+                       fs->fsi_flag = 0;
+                       if (ld_word(fs->win + BS_55AA) == 0xAA55        /* Load FSInfo data if available */
+                               && ld_dword(fs->win + FSI_LeadSig) == 0x41615252
+                               && ld_dword(fs->win + FSI_StrucSig) == 0x61417272)
+                       {
+#if (FF_FS_NOFSINFO & 1) == 0
+                               fs->free_clst = ld_dword(fs->win + FSI_Free_Count);
+#endif
+#if (FF_FS_NOFSINFO & 2) == 0
+                               fs->last_clst = ld_dword(fs->win + FSI_Nxt_Free);
+#endif
+                       }
+               }
+#endif /* (FF_FS_NOFSINFO & 3) != 3 */
+#endif /* !FF_FS_READONLY */
+       }
+
+       fs->fs_type = (BYTE)fmt;/* FAT sub-type */
+       fs->id = ++Fsid;                /* Volume mount ID */
+#if FF_USE_LFN == 1
+       fs->lfnbuf = LfnBuf;    /* Static LFN working buffer */
+#if FF_FS_EXFAT
+       fs->dirbuf = DirBuf;    /* Static directory block scratchpad buuffer */
+#endif
+#endif
+#if FF_FS_RPATH != 0
+       fs->cdir = 0;                   /* Initialize current directory */
+#endif
+#if FF_FS_LOCK != 0                    /* Clear file lock semaphores */
+       clear_lock(fs);
+#endif
+       return FR_OK;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Check if the file/directory object is valid or not                    */
+/*-----------------------------------------------------------------------*/
+
+static FRESULT validate (      /* Returns FR_OK or FR_INVALID_OBJECT */
+       FFOBJID* obj,                   /* Pointer to the FFOBJID, the 1st member in the FIL/DIR object, to check validity */
+       FATFS** rfs                             /* Pointer to pointer to the owner filesystem object to return */
+)
+{
+       FRESULT res = FR_INVALID_OBJECT;
+
+
+       if (obj && obj->fs && obj->fs->fs_type && obj->id == obj->fs->id) {     /* Test if the object is valid */
+#if FF_FS_REENTRANT
+               if (lock_fs(obj->fs)) { /* Obtain the filesystem object */
+                       if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */
+                               res = FR_OK;
+                       } else {
+                               unlock_fs(obj->fs, FR_OK);
+                       }
+               } else {
+                       res = FR_TIMEOUT;
+               }
+#else
+               if (!(disk_status(obj->fs->pdrv) & STA_NOINIT)) { /* Test if the phsical drive is kept initialized */
+                       res = FR_OK;
+               }
+#endif
+       }
+       *rfs = (res == FR_OK) ? obj->fs : 0;    /* Corresponding filesystem object */
+       return res;
+}
+
+
+
+
+/*---------------------------------------------------------------------------
+
+   Public Functions (FatFs API)
+
+----------------------------------------------------------------------------*/
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Mount/Unmount a Logical Drive                                         */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mount (
+       FATFS* fs,                      /* Pointer to the filesystem object (NULL:unmount)*/
+       const TCHAR* path,      /* Logical drive number to be mounted/unmounted */
+       BYTE opt                        /* Mode option 0:Do not mount (delayed mount), 1:Mount immediately */
+)
+{
+       FATFS *cfs;
+       int vol;
+       FRESULT res;
+       const TCHAR *rp = path;
+
+
+       /* Get logical drive number */
+       vol = get_ldnumber(&rp);
+       if (vol < 0) return FR_INVALID_DRIVE;
+       cfs = FatFs[vol];                                       /* Pointer to fs object */
+
+       if (cfs) {
+#if FF_FS_LOCK != 0
+               clear_lock(cfs);
+#endif
+#if FF_FS_REENTRANT                                            /* Discard sync object of the current volume */
+               if (!ff_del_syncobj(cfs->sobj)) return FR_INT_ERR;
+#endif
+               cfs->fs_type = 0;                               /* Clear old fs object */
+       }
+
+       if (fs) {
+               fs->fs_type = 0;                                /* Clear new fs object */
+#if FF_FS_REENTRANT                                            /* Create sync object for the new volume */
+               if (!ff_cre_syncobj((BYTE)vol, &fs->sobj)) return FR_INT_ERR;
+#endif
+       }
+       FatFs[vol] = fs;                                        /* Register new fs object */
+
+       if (opt == 0) return FR_OK;                     /* Do not mount now, it will be mounted later */
+
+       res = mount_volume(&path, &fs, 0);      /* Force mounted the volume */
+       LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Open or Create a File                                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_open (
+       FIL* fp,                        /* Pointer to the blank file object */
+       const TCHAR* path,      /* Pointer to the file name */
+       BYTE mode                       /* Access mode and file open mode flags */
+)
+{
+       FRESULT res;
+       DIR dj;
+       FATFS *fs;
+#if !FF_FS_READONLY
+       DWORD cl, bcs, clst;
+       LBA_t sc;
+       FSIZE_t ofs;
+#endif
+       DEF_NAMBUF
+
+
+       if (!fp) return FR_INVALID_OBJECT;
+
+       /* Get logical drive number */
+       mode &= FF_FS_READONLY ? FA_READ : FA_READ | FA_WRITE | FA_CREATE_ALWAYS | FA_CREATE_NEW | FA_OPEN_ALWAYS | FA_OPEN_APPEND;
+       res = mount_volume(&path, &fs, mode);
+       if (res == FR_OK) {
+               dj.obj.fs = fs;
+               INIT_NAMBUF(fs);
+               res = follow_path(&dj, path);   /* Follow the file path */
+#if !FF_FS_READONLY    /* Read/Write configuration */
+               if (res == FR_OK) {
+                       if (dj.fn[NSFLAG] & NS_NONAME) {        /* Origin directory itself? */
+                               res = FR_INVALID_NAME;
+                       }
+#if FF_FS_LOCK != 0
+                       else {
+                               res = chk_lock(&dj, (mode & ~FA_READ) ? 1 : 0);         /* Check if the file can be used */
+                       }
+#endif
+               }
+               /* Create or Open a file */
+               if (mode & (FA_CREATE_ALWAYS | FA_OPEN_ALWAYS | FA_CREATE_NEW)) {
+                       if (res != FR_OK) {                                     /* No file, create new */
+                               if (res == FR_NO_FILE) {                /* There is no file to open, create a new entry */
+#if FF_FS_LOCK != 0
+                                       res = enq_lock() ? dir_register(&dj) : FR_TOO_MANY_OPEN_FILES;
+#else
+                                       res = dir_register(&dj);
+#endif
+                               }
+                               mode |= FA_CREATE_ALWAYS;               /* File is created */
+                       }
+                       else {                                                          /* Any object with the same name is already existing */
+                               if (dj.obj.attr & (AM_RDO | AM_DIR)) {  /* Cannot overwrite it (R/O or DIR) */
+                                       res = FR_DENIED;
+                               } else {
+                                       if (mode & FA_CREATE_NEW) res = FR_EXIST;       /* Cannot create as new file */
+                               }
+                       }
+                       if (res == FR_OK && (mode & FA_CREATE_ALWAYS)) {        /* Truncate the file if overwrite mode */
+#if FF_FS_EXFAT
+                               if (fs->fs_type == FS_EXFAT) {
+                                       /* Get current allocation info */
+                                       fp->obj.fs = fs;
+                                       init_alloc_info(fs, &fp->obj);
+                                       /* Set directory entry block initial state */
+                                       mem_set(fs->dirbuf + 2, 0, 30);         /* Clear 85 entry except for NumSec */
+                                       mem_set(fs->dirbuf + 38, 0, 26);        /* Clear C0 entry except for NumName and NameHash */
+                                       fs->dirbuf[XDIR_Attr] = AM_ARC;
+                                       st_dword(fs->dirbuf + XDIR_CrtTime, GET_FATTIME());
+                                       fs->dirbuf[XDIR_GenFlags] = 1;
+                                       res = store_xdir(&dj);
+                                       if (res == FR_OK && fp->obj.sclust != 0) {      /* Remove the cluster chain if exist */
+                                               res = remove_chain(&fp->obj, fp->obj.sclust, 0);
+                                               fs->last_clst = fp->obj.sclust - 1;             /* Reuse the cluster hole */
+                                       }
+                               } else
+#endif
+                               {
+                                       /* Set directory entry initial state */
+                                       cl = ld_clust(fs, dj.dir);                      /* Get current cluster chain */
+                                       st_dword(dj.dir + DIR_CrtTime, GET_FATTIME());  /* Set created time */
+                                       dj.dir[DIR_Attr] = AM_ARC;                      /* Reset attribute */
+                                       st_clust(fs, dj.dir, 0);                        /* Reset file allocation info */
+                                       st_dword(dj.dir + DIR_FileSize, 0);
+                                       fs->wflag = 1;
+                                       if (cl != 0) {                                          /* Remove the cluster chain if exist */
+                                               sc = fs->winsect;
+                                               res = remove_chain(&dj.obj, cl, 0);
+                                               if (res == FR_OK) {
+                                                       res = move_window(fs, sc);
+                                                       fs->last_clst = cl - 1;         /* Reuse the cluster hole */
+                                               }
+                                       }
+                               }
+                       }
+               }
+               else {  /* Open an existing file */
+                       if (res == FR_OK) {                                     /* Is the object exsiting? */
+                               if (dj.obj.attr & AM_DIR) {             /* File open against a directory */
+                                       res = FR_NO_FILE;
+                               } else {
+                                       if ((mode & FA_WRITE) && (dj.obj.attr & AM_RDO)) { /* Write mode open against R/O file */
+                                               res = FR_DENIED;
+                                       }
+                               }
+                       }
+               }
+               if (res == FR_OK) {
+                       if (mode & FA_CREATE_ALWAYS) mode |= FA_MODIFIED;       /* Set file change flag if created or overwritten */
+                       fp->dir_sect = fs->winsect;                     /* Pointer to the directory entry */
+                       fp->dir_ptr = dj.dir;
+#if FF_FS_LOCK != 0
+                       fp->obj.lockid = inc_lock(&dj, (mode & ~FA_READ) ? 1 : 0);      /* Lock the file for this session */
+                       if (fp->obj.lockid == 0) res = FR_INT_ERR;
+#endif
+               }
+#else          /* R/O configuration */
+               if (res == FR_OK) {
+                       if (dj.fn[NSFLAG] & NS_NONAME) {        /* Is it origin directory itself? */
+                               res = FR_INVALID_NAME;
+                       } else {
+                               if (dj.obj.attr & AM_DIR) {             /* Is it a directory? */
+                                       res = FR_NO_FILE;
+                               }
+                       }
+               }
+#endif
+
+               if (res == FR_OK) {
+#if FF_FS_EXFAT
+                       if (fs->fs_type == FS_EXFAT) {
+                               fp->obj.c_scl = dj.obj.sclust;                                                  /* Get containing directory info */
+                               fp->obj.c_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat;
+                               fp->obj.c_ofs = dj.blk_ofs;
+                               init_alloc_info(fs, &fp->obj);
+                       } else
+#endif
+                       {
+                               fp->obj.sclust = ld_clust(fs, dj.dir);                                  /* Get object allocation info */
+                               fp->obj.objsize = ld_dword(dj.dir + DIR_FileSize);
+                       }
+#if FF_USE_FASTSEEK
+                       fp->cltbl = 0;                  /* Disable fast seek mode */
+#endif
+                       fp->obj.fs = fs;                /* Validate the file object */
+                       fp->obj.id = fs->id;
+                       fp->flag = mode;                /* Set file access mode */
+                       fp->err = 0;                    /* Clear error flag */
+                       fp->sect = 0;                   /* Invalidate current data sector */
+                       fp->fptr = 0;                   /* Set file pointer top of the file */
+#if !FF_FS_READONLY
+#if !FF_FS_TINY
+                       mem_set(fp->buf, 0, sizeof fp->buf);    /* Clear sector buffer */
+#endif
+                       if ((mode & FA_SEEKEND) && fp->obj.objsize > 0) {       /* Seek to end of file if FA_OPEN_APPEND is specified */
+                               fp->fptr = fp->obj.objsize;                     /* Offset to seek */
+                               bcs = (DWORD)fs->csize * SS(fs);        /* Cluster size in byte */
+                               clst = fp->obj.sclust;                          /* Follow the cluster chain */
+                               for (ofs = fp->obj.objsize; res == FR_OK && ofs > bcs; ofs -= bcs) {
+                                       clst = get_fat(&fp->obj, clst);
+                                       if (clst <= 1) res = FR_INT_ERR;
+                                       if (clst == 0xFFFFFFFF) res = FR_DISK_ERR;
+                               }
+                               fp->clust = clst;
+                               if (res == FR_OK && ofs % SS(fs)) {     /* Fill sector buffer if not on the sector boundary */
+                                       sc = clst2sect(fs, clst);
+                                       if (sc == 0) {
+                                               res = FR_INT_ERR;
+                                       } else {
+                                               fp->sect = sc + (DWORD)(ofs / SS(fs));
+#if !FF_FS_TINY
+                                               if (disk_read(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) res = FR_DISK_ERR;
+#endif
+                                       }
+                               }
+                       }
+#endif
+               }
+
+               FREE_NAMBUF();
+       }
+
+       if (res != FR_OK) fp->obj.fs = 0;       /* Invalidate file object on error */
+
+       LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read File                                                             */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_read (
+       FIL* fp,        /* Pointer to the file object */
+       void* buff,     /* Pointer to data buffer */
+       UINT btr,       /* Number of bytes to read */
+       UINT* br        /* Pointer to number of bytes read */
+)
+{
+       FRESULT res;
+       FATFS *fs;
+       DWORD clst;
+       LBA_t sect;
+       FSIZE_t remain;
+       UINT rcnt, cc, csect;
+       BYTE *rbuff = (BYTE*)buff;
+
+
+       *br = 0;        /* Clear read byte counter */
+       res = validate(&fp->obj, &fs);                          /* Check validity of the file object */
+       if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);       /* Check validity */
+       if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED); /* Check access mode */
+       remain = fp->obj.objsize - fp->fptr;
+       if (btr > remain) btr = (UINT)remain;           /* Truncate btr by remaining bytes */
+
+       for ( ;  btr;                                                           /* Repeat until btr bytes read */
+               btr -= rcnt, *br += rcnt, rbuff += rcnt, fp->fptr += rcnt) {
+               if (fp->fptr % SS(fs) == 0) {                   /* On the sector boundary? */
+                       csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1));    /* Sector offset in the cluster */
+                       if (csect == 0) {                                       /* On the cluster boundary? */
+                               if (fp->fptr == 0) {                    /* On the top of the file? */
+                                       clst = fp->obj.sclust;          /* Follow cluster chain from the origin */
+                               } else {                                                /* Middle or end of the file */
+#if FF_USE_FASTSEEK
+                                       if (fp->cltbl) {
+                                               clst = clmt_clust(fp, fp->fptr);        /* Get cluster# from the CLMT */
+                                       } else
+#endif
+                                       {
+                                               clst = get_fat(&fp->obj, fp->clust);    /* Follow cluster chain on the FAT */
+                                       }
+                               }
+                               if (clst < 2) ABORT(fs, FR_INT_ERR);
+                               if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
+                               fp->clust = clst;                               /* Update current cluster */
+                       }
+                       sect = clst2sect(fs, fp->clust);        /* Get current sector */
+                       if (sect == 0) ABORT(fs, FR_INT_ERR);
+                       sect += csect;
+                       cc = btr / SS(fs);                                      /* When remaining bytes >= sector size, */
+                       if (cc > 0) {                                           /* Read maximum contiguous sectors directly */
+                               if (csect + cc > fs->csize) {   /* Clip at cluster boundary */
+                                       cc = fs->csize - csect;
+                               }
+                               if (disk_read(fs->pdrv, rbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR);
+#if !FF_FS_READONLY && FF_FS_MINIMIZE <= 2             /* Replace one of the read sectors with cached data if it contains a dirty sector */
+#if FF_FS_TINY
+                               if (fs->wflag && fs->winsect - sect < cc) {
+                                       mem_cpy(rbuff + ((fs->winsect - sect) * SS(fs)), fs->win, SS(fs));
+                               }
+#else
+                               if ((fp->flag & FA_DIRTY) && fp->sect - sect < cc) {
+                                       mem_cpy(rbuff + ((fp->sect - sect) * SS(fs)), fp->buf, SS(fs));
+                               }
+#endif
+#endif
+                               rcnt = SS(fs) * cc;                             /* Number of bytes transferred */
+                               continue;
+                       }
+#if !FF_FS_TINY
+                       if (fp->sect != sect) {                 /* Load data sector if not in cache */
+#if !FF_FS_READONLY
+                               if (fp->flag & FA_DIRTY) {              /* Write-back dirty sector cache */
+                                       if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
+                                       fp->flag &= (BYTE)~FA_DIRTY;
+                               }
+#endif
+                               if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK)    ABORT(fs, FR_DISK_ERR); /* Fill sector cache */
+                       }
+#endif
+                       fp->sect = sect;
+               }
+               rcnt = SS(fs) - (UINT)fp->fptr % SS(fs);        /* Number of bytes remains in the sector */
+               if (rcnt > btr) rcnt = btr;                                     /* Clip it by btr if needed */
+#if FF_FS_TINY
+               if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */
+               mem_cpy(rbuff, fs->win + fp->fptr % SS(fs), rcnt);      /* Extract partial sector */
+#else
+               mem_cpy(rbuff, fp->buf + fp->fptr % SS(fs), rcnt);      /* Extract partial sector */
+#endif
+       }
+
+       LEAVE_FF(fs, FR_OK);
+}
+
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Write File                                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_write (
+       FIL* fp,                        /* Pointer to the file object */
+       const void* buff,       /* Pointer to the data to be written */
+       UINT btw,                       /* Number of bytes to write */
+       UINT* bw                        /* Pointer to number of bytes written */
+)
+{
+       FRESULT res;
+       FATFS *fs;
+       DWORD clst;
+       LBA_t sect;
+       UINT wcnt, cc, csect;
+       const BYTE *wbuff = (const BYTE*)buff;
+
+
+       *bw = 0;        /* Clear write byte counter */
+       res = validate(&fp->obj, &fs);                  /* Check validity of the file object */
+       if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);       /* Check validity */
+       if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED);    /* Check access mode */
+
+       /* Check fptr wrap-around (file size cannot reach 4 GiB at FAT volume) */
+       if ((!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) && (DWORD)(fp->fptr + btw) < (DWORD)fp->fptr) {
+               btw = (UINT)(0xFFFFFFFF - (DWORD)fp->fptr);
+       }
+
+       for ( ;  btw;                                                   /* Repeat until all data written */
+               btw -= wcnt, *bw += wcnt, wbuff += wcnt, fp->fptr += wcnt, fp->obj.objsize = (fp->fptr > fp->obj.objsize) ? fp->fptr : fp->obj.objsize) {
+               if (fp->fptr % SS(fs) == 0) {           /* On the sector boundary? */
+                       csect = (UINT)(fp->fptr / SS(fs)) & (fs->csize - 1);    /* Sector offset in the cluster */
+                       if (csect == 0) {                               /* On the cluster boundary? */
+                               if (fp->fptr == 0) {            /* On the top of the file? */
+                                       clst = fp->obj.sclust;  /* Follow from the origin */
+                                       if (clst == 0) {                /* If no cluster is allocated, */
+                                               clst = create_chain(&fp->obj, 0);       /* create a new cluster chain */
+                                       }
+                               } else {                                        /* On the middle or end of the file */
+#if FF_USE_FASTSEEK
+                                       if (fp->cltbl) {
+                                               clst = clmt_clust(fp, fp->fptr);        /* Get cluster# from the CLMT */
+                                       } else
+#endif
+                                       {
+                                               clst = create_chain(&fp->obj, fp->clust);       /* Follow or stretch cluster chain on the FAT */
+                                       }
+                               }
+                               if (clst == 0) break;           /* Could not allocate a new cluster (disk full) */
+                               if (clst == 1) ABORT(fs, FR_INT_ERR);
+                               if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
+                               fp->clust = clst;                       /* Update current cluster */
+                               if (fp->obj.sclust == 0) fp->obj.sclust = clst; /* Set start cluster if the first write */
+                       }
+#if FF_FS_TINY
+                       if (fs->winsect == fp->sect && sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR);        /* Write-back sector cache */
+#else
+                       if (fp->flag & FA_DIRTY) {              /* Write-back sector cache */
+                               if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
+                               fp->flag &= (BYTE)~FA_DIRTY;
+                       }
+#endif
+                       sect = clst2sect(fs, fp->clust);        /* Get current sector */
+                       if (sect == 0) ABORT(fs, FR_INT_ERR);
+                       sect += csect;
+                       cc = btw / SS(fs);                              /* When remaining bytes >= sector size, */
+                       if (cc > 0) {                                   /* Write maximum contiguous sectors directly */
+                               if (csect + cc > fs->csize) {   /* Clip at cluster boundary */
+                                       cc = fs->csize - csect;
+                               }
+                               if (disk_write(fs->pdrv, wbuff, sect, cc) != RES_OK) ABORT(fs, FR_DISK_ERR);
+#if FF_FS_MINIMIZE <= 2
+#if FF_FS_TINY
+                               if (fs->winsect - sect < cc) {  /* Refill sector cache if it gets invalidated by the direct write */
+                                       mem_cpy(fs->win, wbuff + ((fs->winsect - sect) * SS(fs)), SS(fs));
+                                       fs->wflag = 0;
+                               }
+#else
+                               if (fp->sect - sect < cc) { /* Refill sector cache if it gets invalidated by the direct write */
+                                       mem_cpy(fp->buf, wbuff + ((fp->sect - sect) * SS(fs)), SS(fs));
+                                       fp->flag &= (BYTE)~FA_DIRTY;
+                               }
+#endif
+#endif
+                               wcnt = SS(fs) * cc;             /* Number of bytes transferred */
+                               continue;
+                       }
+#if FF_FS_TINY
+                       if (fp->fptr >= fp->obj.objsize) {      /* Avoid silly cache filling on the growing edge */
+                               if (sync_window(fs) != FR_OK) ABORT(fs, FR_DISK_ERR);
+                               fs->winsect = sect;
+                       }
+#else
+                       if (fp->sect != sect &&                 /* Fill sector cache with file data */
+                               fp->fptr < fp->obj.objsize &&
+                               disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) {
+                                       ABORT(fs, FR_DISK_ERR);
+                       }
+#endif
+                       fp->sect = sect;
+               }
+               wcnt = SS(fs) - (UINT)fp->fptr % SS(fs);        /* Number of bytes remains in the sector */
+               if (wcnt > btw) wcnt = btw;                                     /* Clip it by btw if needed */
+#if FF_FS_TINY
+               if (move_window(fs, fp->sect) != FR_OK) ABORT(fs, FR_DISK_ERR); /* Move sector window */
+               mem_cpy(fs->win + fp->fptr % SS(fs), wbuff, wcnt);      /* Fit data to the sector */
+               fs->wflag = 1;
+#else
+               mem_cpy(fp->buf + fp->fptr % SS(fs), wbuff, wcnt);      /* Fit data to the sector */
+               fp->flag |= FA_DIRTY;
+#endif
+       }
+
+       fp->flag |= FA_MODIFIED;                                /* Set file change flag */
+
+       LEAVE_FF(fs, FR_OK);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Synchronize the File                                                  */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_sync (
+       FIL* fp         /* Pointer to the file object */
+)
+{
+       FRESULT res;
+       FATFS *fs;
+       DWORD tm;
+       BYTE *dir;
+
+
+       res = validate(&fp->obj, &fs);  /* Check validity of the file object */
+       if (res == FR_OK) {
+               if (fp->flag & FA_MODIFIED) {   /* Is there any change to the file? */
+#if !FF_FS_TINY
+                       if (fp->flag & FA_DIRTY) {      /* Write-back cached data if needed */
+                               if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) LEAVE_FF(fs, FR_DISK_ERR);
+                               fp->flag &= (BYTE)~FA_DIRTY;
+                       }
+#endif
+                       /* Update the directory entry */
+                       tm = GET_FATTIME();                             /* Modified time */
+#if FF_FS_EXFAT
+                       if (fs->fs_type == FS_EXFAT) {
+                               res = fill_first_frag(&fp->obj);        /* Fill first fragment on the FAT if needed */
+                               if (res == FR_OK) {
+                                       res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF);  /* Fill last fragment on the FAT if needed */
+                               }
+                               if (res == FR_OK) {
+                                       DIR dj;
+                                       DEF_NAMBUF
+
+                                       INIT_NAMBUF(fs);
+                                       res = load_obj_xdir(&dj, &fp->obj);     /* Load directory entry block */
+                                       if (res == FR_OK) {
+                                               fs->dirbuf[XDIR_Attr] |= AM_ARC;                                /* Set archive attribute to indicate that the file has been changed */
+                                               fs->dirbuf[XDIR_GenFlags] = fp->obj.stat | 1;   /* Update file allocation information */
+                                               st_dword(fs->dirbuf + XDIR_FstClus, fp->obj.sclust);            /* Update start cluster */
+                                               st_qword(fs->dirbuf + XDIR_FileSize, fp->obj.objsize);          /* Update file size */
+                                               st_qword(fs->dirbuf + XDIR_ValidFileSize, fp->obj.objsize);     /* (FatFs does not support Valid File Size feature) */
+                                               st_dword(fs->dirbuf + XDIR_ModTime, tm);                /* Update modified time */
+                                               fs->dirbuf[XDIR_ModTime10] = 0;
+                                               st_dword(fs->dirbuf + XDIR_AccTime, 0);
+                                               res = store_xdir(&dj);  /* Restore it to the directory */
+                                               if (res == FR_OK) {
+                                                       res = sync_fs(fs);
+                                                       fp->flag &= (BYTE)~FA_MODIFIED;
+                                               }
+                                       }
+                                       FREE_NAMBUF();
+                               }
+                       } else
+#endif
+                       {
+                               res = move_window(fs, fp->dir_sect);
+                               if (res == FR_OK) {
+                                       dir = fp->dir_ptr;
+                                       dir[DIR_Attr] |= AM_ARC;                                                /* Set archive attribute to indicate that the file has been changed */
+                                       st_clust(fp->obj.fs, dir, fp->obj.sclust);              /* Update file allocation information  */
+                                       st_dword(dir + DIR_FileSize, (DWORD)fp->obj.objsize);   /* Update file size */
+                                       st_dword(dir + DIR_ModTime, tm);                                /* Update modified time */
+                                       st_word(dir + DIR_LstAccDate, 0);
+                                       fs->wflag = 1;
+                                       res = sync_fs(fs);                                      /* Restore it to the directory */
+                                       fp->flag &= (BYTE)~FA_MODIFIED;
+                               }
+                       }
+               }
+       }
+
+       LEAVE_FF(fs, res);
+}
+
+#endif /* !FF_FS_READONLY */
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Close File                                                            */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_close (
+       FIL* fp         /* Pointer to the file object to be closed */
+)
+{
+       FRESULT res;
+       FATFS *fs;
+
+#if !FF_FS_READONLY
+       res = f_sync(fp);                                       /* Flush cached data */
+       if (res == FR_OK)
+#endif
+       {
+               res = validate(&fp->obj, &fs);  /* Lock volume */
+               if (res == FR_OK) {
+#if FF_FS_LOCK != 0
+                       res = dec_lock(fp->obj.lockid);         /* Decrement file open counter */
+                       if (res == FR_OK) fp->obj.fs = 0;       /* Invalidate file object */
+#else
+                       fp->obj.fs = 0; /* Invalidate file object */
+#endif
+#if FF_FS_REENTRANT
+                       unlock_fs(fs, FR_OK);           /* Unlock volume */
+#endif
+               }
+       }
+       return res;
+}
+
+
+
+
+#if FF_FS_RPATH >= 1
+/*-----------------------------------------------------------------------*/
+/* Change Current Directory or Current Drive, Get Current Directory      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_chdrive (
+       const TCHAR* path               /* Drive number to set */
+)
+{
+       int vol;
+
+
+       /* Get logical drive number */
+       vol = get_ldnumber(&path);
+       if (vol < 0) return FR_INVALID_DRIVE;
+       CurrVol = (BYTE)vol;    /* Set it as current volume */
+
+       return FR_OK;
+}
+
+
+
+FRESULT f_chdir (
+       const TCHAR* path       /* Pointer to the directory path */
+)
+{
+#if FF_STR_VOLUME_ID == 2
+       UINT i;
+#endif
+       FRESULT res;
+       DIR dj;
+       FATFS *fs;
+       DEF_NAMBUF
+
+
+       /* Get logical drive */
+       res = mount_volume(&path, &fs, 0);
+       if (res == FR_OK) {
+               dj.obj.fs = fs;
+               INIT_NAMBUF(fs);
+               res = follow_path(&dj, path);           /* Follow the path */
+               if (res == FR_OK) {                                     /* Follow completed */
+                       if (dj.fn[NSFLAG] & NS_NONAME) {        /* Is it the start directory itself? */
+                               fs->cdir = dj.obj.sclust;
+#if FF_FS_EXFAT
+                               if (fs->fs_type == FS_EXFAT) {
+                                       fs->cdc_scl = dj.obj.c_scl;
+                                       fs->cdc_size = dj.obj.c_size;
+                                       fs->cdc_ofs = dj.obj.c_ofs;
+                               }
+#endif
+                       } else {
+                               if (dj.obj.attr & AM_DIR) {     /* It is a sub-directory */
+#if FF_FS_EXFAT
+                                       if (fs->fs_type == FS_EXFAT) {
+                                               fs->cdir = ld_dword(fs->dirbuf + XDIR_FstClus);         /* Sub-directory cluster */
+                                               fs->cdc_scl = dj.obj.sclust;                                            /* Save containing directory information */
+                                               fs->cdc_size = ((DWORD)dj.obj.objsize & 0xFFFFFF00) | dj.obj.stat;
+                                               fs->cdc_ofs = dj.blk_ofs;
+                                       } else
+#endif
+                                       {
+                                               fs->cdir = ld_clust(fs, dj.dir);                                        /* Sub-directory cluster */
+                                       }
+                               } else {
+                                       res = FR_NO_PATH;               /* Reached but a file */
+                               }
+                       }
+               }
+               FREE_NAMBUF();
+               if (res == FR_NO_FILE) res = FR_NO_PATH;
+#if FF_STR_VOLUME_ID == 2      /* Also current drive is changed at Unix style volume ID */
+               if (res == FR_OK) {
+                       for (i = FF_VOLUMES - 1; i && fs != FatFs[i]; i--) ;    /* Set current drive */
+                       CurrVol = (BYTE)i;
+               }
+#endif
+       }
+
+       LEAVE_FF(fs, res);
+}
+
+
+#if FF_FS_RPATH >= 2
+FRESULT f_getcwd (
+       TCHAR* buff,    /* Pointer to the directory path */
+       UINT len                /* Size of buff in unit of TCHAR */
+)
+{
+       FRESULT res;
+       DIR dj;
+       FATFS *fs;
+       UINT i, n;
+       DWORD ccl;
+       TCHAR *tp = buff;
+#if FF_VOLUMES >= 2
+       UINT vl;
+#if FF_STR_VOLUME_ID
+       const char *vp;
+#endif
+#endif
+       FILINFO fno;
+       DEF_NAMBUF
+
+
+       /* Get logical drive */
+       buff[0] = 0;    /* Set null string to get current volume */
+       res = mount_volume((const TCHAR**)&buff, &fs, 0);       /* Get current volume */
+       if (res == FR_OK) {
+               dj.obj.fs = fs;
+               INIT_NAMBUF(fs);
+
+               /* Follow parent directories and create the path */
+               i = len;                        /* Bottom of buffer (directory stack base) */
+               if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) {  /* (Cannot do getcwd on exFAT and returns root path) */
+                       dj.obj.sclust = fs->cdir;                               /* Start to follow upper directory from current directory */
+                       while ((ccl = dj.obj.sclust) != 0) {    /* Repeat while current directory is a sub-directory */
+                               res = dir_sdi(&dj, 1 * SZDIRE); /* Get parent directory */
+                               if (res != FR_OK) break;
+                               res = move_window(fs, dj.sect);
+                               if (res != FR_OK) break;
+                               dj.obj.sclust = ld_clust(fs, dj.dir);   /* Goto parent directory */
+                               res = dir_sdi(&dj, 0);
+                               if (res != FR_OK) break;
+                               do {                                                    /* Find the entry links to the child directory */
+                                       res = DIR_READ_FILE(&dj);
+                                       if (res != FR_OK) break;
+                                       if (ccl == ld_clust(fs, dj.dir)) break; /* Found the entry */
+                                       res = dir_next(&dj, 0);
+                               } while (res == FR_OK);
+                               if (res == FR_NO_FILE) res = FR_INT_ERR;/* It cannot be 'not found'. */
+                               if (res != FR_OK) break;
+                               get_fileinfo(&dj, &fno);                /* Get the directory name and push it to the buffer */
+                               for (n = 0; fno.fname[n]; n++) ;        /* Name length */
+                               if (i < n + 1) {        /* Insufficient space to store the path name? */
+                                       res = FR_NOT_ENOUGH_CORE; break;
+                               }
+                               while (n) buff[--i] = fno.fname[--n];   /* Stack the name */
+                               buff[--i] = '/';
+                       }
+               }
+               if (res == FR_OK) {
+                       if (i == len) buff[--i] = '/';  /* Is it the root-directory? */
+#if FF_VOLUMES >= 2                    /* Put drive prefix */
+                       vl = 0;
+#if FF_STR_VOLUME_ID >= 1      /* String volume ID */
+                       for (n = 0, vp = (const char*)VolumeStr[CurrVol]; vp[n]; n++) ;
+                       if (i >= n + 2) {
+                               if (FF_STR_VOLUME_ID == 2) *tp++ = (TCHAR)'/';
+                               for (vl = 0; vl < n; *tp++ = (TCHAR)vp[vl], vl++) ;
+                               if (FF_STR_VOLUME_ID == 1) *tp++ = (TCHAR)':';
+                               vl++;
+                       }
+#else                                          /* Numeric volume ID */
+                       if (i >= 3) {
+                               *tp++ = (TCHAR)'0' + CurrVol;
+                               *tp++ = (TCHAR)':';
+                               vl = 2;
+                       }
+#endif
+                       if (vl == 0) res = FR_NOT_ENOUGH_CORE;
+#endif
+                       /* Add current directory path */
+                       if (res == FR_OK) {
+                               do *tp++ = buff[i++]; while (i < len);  /* Copy stacked path string */
+                       }
+               }
+               FREE_NAMBUF();
+       }
+
+       *tp = 0;
+       LEAVE_FF(fs, res);
+}
+
+#endif /* FF_FS_RPATH >= 2 */
+#endif /* FF_FS_RPATH >= 1 */
+
+
+
+#if FF_FS_MINIMIZE <= 2
+/*-----------------------------------------------------------------------*/
+/* Seek File Read/Write Pointer                                          */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_lseek (
+       FIL* fp,                /* Pointer to the file object */
+       FSIZE_t ofs             /* File pointer from top of file */
+)
+{
+       FRESULT res;
+       FATFS *fs;
+       DWORD clst, bcs;
+       LBA_t nsect;
+       FSIZE_t ifptr;
+#if FF_USE_FASTSEEK
+       DWORD cl, pcl, ncl, tcl, tlen, ulen, *tbl;
+       LBA_t dsc;
+#endif
+
+       res = validate(&fp->obj, &fs);          /* Check validity of the file object */
+       if (res == FR_OK) res = (FRESULT)fp->err;
+#if FF_FS_EXFAT && !FF_FS_READONLY
+       if (res == FR_OK && fs->fs_type == FS_EXFAT) {
+               res = fill_last_frag(&fp->obj, fp->clust, 0xFFFFFFFF);  /* Fill last fragment on the FAT if needed */
+       }
+#endif
+       if (res != FR_OK) LEAVE_FF(fs, res);
+
+#if FF_USE_FASTSEEK
+       if (fp->cltbl) {        /* Fast seek */
+               if (ofs == CREATE_LINKMAP) {    /* Create CLMT */
+                       tbl = fp->cltbl;
+                       tlen = *tbl++; ulen = 2;        /* Given table size and required table size */
+                       cl = fp->obj.sclust;            /* Origin of the chain */
+                       if (cl != 0) {
+                               do {
+                                       /* Get a fragment */
+                                       tcl = cl; ncl = 0; ulen += 2;   /* Top, length and used items */
+                                       do {
+                                               pcl = cl; ncl++;
+                                               cl = get_fat(&fp->obj, cl);
+                                               if (cl <= 1) ABORT(fs, FR_INT_ERR);
+                                               if (cl == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
+                                       } while (cl == pcl + 1);
+                                       if (ulen <= tlen) {             /* Store the length and top of the fragment */
+                                               *tbl++ = ncl; *tbl++ = tcl;
+                                       }
+                               } while (cl < fs->n_fatent);    /* Repeat until end of chain */
+                       }
+                       *fp->cltbl = ulen;      /* Number of items used */
+                       if (ulen <= tlen) {
+                               *tbl = 0;               /* Terminate table */
+                       } else {
+                               res = FR_NOT_ENOUGH_CORE;       /* Given table size is smaller than required */
+                       }
+               } else {                                                /* Fast seek */
+                       if (ofs > fp->obj.objsize) ofs = fp->obj.objsize;       /* Clip offset at the file size */
+                       fp->fptr = ofs;                         /* Set file pointer */
+                       if (ofs > 0) {
+                               fp->clust = clmt_clust(fp, ofs - 1);
+                               dsc = clst2sect(fs, fp->clust);
+                               if (dsc == 0) ABORT(fs, FR_INT_ERR);
+                               dsc += (DWORD)((ofs - 1) / SS(fs)) & (fs->csize - 1);
+                               if (fp->fptr % SS(fs) && dsc != fp->sect) {     /* Refill sector cache if needed */
+#if !FF_FS_TINY
+#if !FF_FS_READONLY
+                                       if (fp->flag & FA_DIRTY) {              /* Write-back dirty sector cache */
+                                               if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
+                                               fp->flag &= (BYTE)~FA_DIRTY;
+                                       }
+#endif
+                                       if (disk_read(fs->pdrv, fp->buf, dsc, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);     /* Load current sector */
+#endif
+                                       fp->sect = dsc;
+                               }
+                       }
+               }
+       } else
+#endif
+
+       /* Normal Seek */
+       {
+#if FF_FS_EXFAT
+               if (fs->fs_type != FS_EXFAT && ofs >= 0x100000000) ofs = 0xFFFFFFFF;    /* Clip at 4 GiB - 1 if at FATxx */
+#endif
+               if (ofs > fp->obj.objsize && (FF_FS_READONLY || !(fp->flag & FA_WRITE))) {      /* In read-only mode, clip offset with the file size */
+                       ofs = fp->obj.objsize;
+               }
+               ifptr = fp->fptr;
+               fp->fptr = nsect = 0;
+               if (ofs > 0) {
+                       bcs = (DWORD)fs->csize * SS(fs);        /* Cluster size (byte) */
+                       if (ifptr > 0 &&
+                               (ofs - 1) / bcs >= (ifptr - 1) / bcs) { /* When seek to same or following cluster, */
+                               fp->fptr = (ifptr - 1) & ~(FSIZE_t)(bcs - 1);   /* start from the current cluster */
+                               ofs -= fp->fptr;
+                               clst = fp->clust;
+                       } else {                                                                        /* When seek to back cluster, */
+                               clst = fp->obj.sclust;                                  /* start from the first cluster */
+#if !FF_FS_READONLY
+                               if (clst == 0) {                                                /* If no cluster chain, create a new chain */
+                                       clst = create_chain(&fp->obj, 0);
+                                       if (clst == 1) ABORT(fs, FR_INT_ERR);
+                                       if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
+                                       fp->obj.sclust = clst;
+                               }
+#endif
+                               fp->clust = clst;
+                       }
+                       if (clst != 0) {
+                               while (ofs > bcs) {                                             /* Cluster following loop */
+                                       ofs -= bcs; fp->fptr += bcs;
+#if !FF_FS_READONLY
+                                       if (fp->flag & FA_WRITE) {                      /* Check if in write mode or not */
+                                               if (FF_FS_EXFAT && fp->fptr > fp->obj.objsize) {        /* No FAT chain object needs correct objsize to generate FAT value */
+                                                       fp->obj.objsize = fp->fptr;
+                                                       fp->flag |= FA_MODIFIED;
+                                               }
+                                               clst = create_chain(&fp->obj, clst);    /* Follow chain with forceed stretch */
+                                               if (clst == 0) {                                /* Clip file size in case of disk full */
+                                                       ofs = 0; break;
+                                               }
+                                       } else
+#endif
+                                       {
+                                               clst = get_fat(&fp->obj, clst); /* Follow cluster chain if not in write mode */
+                                       }
+                                       if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
+                                       if (clst <= 1 || clst >= fs->n_fatent) ABORT(fs, FR_INT_ERR);
+                                       fp->clust = clst;
+                               }
+                               fp->fptr += ofs;
+                               if (ofs % SS(fs)) {
+                                       nsect = clst2sect(fs, clst);    /* Current sector */
+                                       if (nsect == 0) ABORT(fs, FR_INT_ERR);
+                                       nsect += (DWORD)(ofs / SS(fs));
+                               }
+                       }
+               }
+               if (!FF_FS_READONLY && fp->fptr > fp->obj.objsize) {    /* Set file change flag if the file size is extended */
+                       fp->obj.objsize = fp->fptr;
+                       fp->flag |= FA_MODIFIED;
+               }
+               if (fp->fptr % SS(fs) && nsect != fp->sect) {   /* Fill sector cache if needed */
+#if !FF_FS_TINY
+#if !FF_FS_READONLY
+                       if (fp->flag & FA_DIRTY) {                      /* Write-back dirty sector cache */
+                               if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
+                               fp->flag &= (BYTE)~FA_DIRTY;
+                       }
+#endif
+                       if (disk_read(fs->pdrv, fp->buf, nsect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);   /* Fill sector cache */
+#endif
+                       fp->sect = nsect;
+               }
+       }
+
+       LEAVE_FF(fs, res);
+}
+
+
+
+#if FF_FS_MINIMIZE <= 1
+/*-----------------------------------------------------------------------*/
+/* Create a Directory Object                                             */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_opendir (
+       DIR* dp,                        /* Pointer to directory object to create */
+       const TCHAR* path       /* Pointer to the directory path */
+)
+{
+       FRESULT res;
+       FATFS *fs;
+       DEF_NAMBUF
+
+
+       if (!dp) return FR_INVALID_OBJECT;
+
+       /* Get logical drive */
+       res = mount_volume(&path, &fs, 0);
+       if (res == FR_OK) {
+               dp->obj.fs = fs;
+               INIT_NAMBUF(fs);
+               res = follow_path(dp, path);                    /* Follow the path to the directory */
+               if (res == FR_OK) {                                             /* Follow completed */
+                       if (!(dp->fn[NSFLAG] & NS_NONAME)) {    /* It is not the origin directory itself */
+                               if (dp->obj.attr & AM_DIR) {            /* This object is a sub-directory */
+#if FF_FS_EXFAT
+                                       if (fs->fs_type == FS_EXFAT) {
+                                               dp->obj.c_scl = dp->obj.sclust;                                                 /* Get containing directory inforamation */
+                                               dp->obj.c_size = ((DWORD)dp->obj.objsize & 0xFFFFFF00) | dp->obj.stat;
+                                               dp->obj.c_ofs = dp->blk_ofs;
+                                               init_alloc_info(fs, &dp->obj);  /* Get object allocation info */
+                                       } else
+#endif
+                                       {
+                                               dp->obj.sclust = ld_clust(fs, dp->dir); /* Get object allocation info */
+                                       }
+                               } else {                                                /* This object is a file */
+                                       res = FR_NO_PATH;
+                               }
+                       }
+                       if (res == FR_OK) {
+                               dp->obj.id = fs->id;
+                               res = dir_sdi(dp, 0);                   /* Rewind directory */
+#if FF_FS_LOCK != 0
+                               if (res == FR_OK) {
+                                       if (dp->obj.sclust != 0) {
+                                               dp->obj.lockid = inc_lock(dp, 0);       /* Lock the sub directory */
+                                               if (!dp->obj.lockid) res = FR_TOO_MANY_OPEN_FILES;
+                                       } else {
+                                               dp->obj.lockid = 0;     /* Root directory need not to be locked */
+                                       }
+                               }
+#endif
+                       }
+               }
+               FREE_NAMBUF();
+               if (res == FR_NO_FILE) res = FR_NO_PATH;
+       }
+       if (res != FR_OK) dp->obj.fs = 0;               /* Invalidate the directory object if function faild */
+
+       LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Close Directory                                                       */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_closedir (
+       DIR *dp         /* Pointer to the directory object to be closed */
+)
+{
+       FRESULT res;
+       FATFS *fs;
+
+
+       res = validate(&dp->obj, &fs);  /* Check validity of the file object */
+       if (res == FR_OK) {
+#if FF_FS_LOCK != 0
+               if (dp->obj.lockid) res = dec_lock(dp->obj.lockid);     /* Decrement sub-directory open counter */
+               if (res == FR_OK) dp->obj.fs = 0;       /* Invalidate directory object */
+#else
+               dp->obj.fs = 0; /* Invalidate directory object */
+#endif
+#if FF_FS_REENTRANT
+               unlock_fs(fs, FR_OK);           /* Unlock volume */
+#endif
+       }
+       return res;
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Read Directory Entries in Sequence                                    */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_readdir (
+       DIR* dp,                        /* Pointer to the open directory object */
+       FILINFO* fno            /* Pointer to file information to return */
+)
+{
+       FRESULT res;
+       FATFS *fs;
+       DEF_NAMBUF
+
+
+       res = validate(&dp->obj, &fs);  /* Check validity of the directory object */
+       if (res == FR_OK) {
+               if (!fno) {
+                       res = dir_sdi(dp, 0);                   /* Rewind the directory object */
+               } else {
+                       INIT_NAMBUF(fs);
+                       res = DIR_READ_FILE(dp);                /* Read an item */
+                       if (res == FR_NO_FILE) res = FR_OK;     /* Ignore end of directory */
+                       if (res == FR_OK) {                             /* A valid entry is found */
+                               get_fileinfo(dp, fno);          /* Get the object information */
+                               res = dir_next(dp, 0);          /* Increment index for next */
+                               if (res == FR_NO_FILE) res = FR_OK;     /* Ignore end of directory now */
+                       }
+                       FREE_NAMBUF();
+               }
+       }
+       LEAVE_FF(fs, res);
+}
+
+
+
+#if FF_USE_FIND
+/*-----------------------------------------------------------------------*/
+/* Find Next File                                                        */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_findnext (
+       DIR* dp,                /* Pointer to the open directory object */
+       FILINFO* fno    /* Pointer to the file information structure */
+)
+{
+       FRESULT res;
+
+
+       for (;;) {
+               res = f_readdir(dp, fno);               /* Get a directory item */
+               if (res != FR_OK || !fno || !fno->fname[0]) break;      /* Terminate if any error or end of directory */
+               if (pattern_matching(dp->pat, fno->fname, 0, 0)) break;         /* Test for the file name */
+#if FF_USE_LFN && FF_USE_FIND == 2
+               if (pattern_matching(dp->pat, fno->altname, 0, 0)) break;       /* Test for alternative name if exist */
+#endif
+       }
+       return res;
+}
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Find First File                                                       */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_findfirst (
+       DIR* dp,                                /* Pointer to the blank directory object */
+       FILINFO* fno,                   /* Pointer to the file information structure */
+       const TCHAR* path,              /* Pointer to the directory to open */
+       const TCHAR* pattern    /* Pointer to the matching pattern */
+)
+{
+       FRESULT res;
+
+
+       dp->pat = pattern;              /* Save pointer to pattern string */
+       res = f_opendir(dp, path);              /* Open the target directory */
+       if (res == FR_OK) {
+               res = f_findnext(dp, fno);      /* Find the first item */
+       }
+       return res;
+}
+
+#endif /* FF_USE_FIND */
+
+
+
+#if FF_FS_MINIMIZE == 0
+/*-----------------------------------------------------------------------*/
+/* Get File Status                                                       */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_stat (
+       const TCHAR* path,      /* Pointer to the file path */
+       FILINFO* fno            /* Pointer to file information to return */
+)
+{
+       FRESULT res;
+       DIR dj;
+       DEF_NAMBUF
+
+
+       /* Get logical drive */
+       res = mount_volume(&path, &dj.obj.fs, 0);
+       if (res == FR_OK) {
+               INIT_NAMBUF(dj.obj.fs);
+               res = follow_path(&dj, path);   /* Follow the file path */
+               if (res == FR_OK) {                             /* Follow completed */
+                       if (dj.fn[NSFLAG] & NS_NONAME) {        /* It is origin directory */
+                               res = FR_INVALID_NAME;
+                       } else {                                                        /* Found an object */
+                               if (fno) get_fileinfo(&dj, fno);
+                       }
+               }
+               FREE_NAMBUF();
+       }
+
+       LEAVE_FF(dj.obj.fs, res);
+}
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Get Number of Free Clusters                                           */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_getfree (
+       const TCHAR* path,      /* Logical drive number */
+       DWORD* nclst,           /* Pointer to a variable to return number of free clusters */
+       FATFS** fatfs           /* Pointer to return pointer to corresponding filesystem object */
+)
+{
+       FRESULT res;
+       FATFS *fs;
+       DWORD nfree, clst, stat;
+       LBA_t sect;
+       UINT i;
+       FFOBJID obj;
+
+
+       /* Get logical drive */
+       res = mount_volume(&path, &fs, 0);
+       if (res == FR_OK) {
+               *fatfs = fs;                            /* Return ptr to the fs object */
+               /* If free_clst is valid, return it without full FAT scan */
+               if (fs->free_clst <= fs->n_fatent - 2) {
+                       *nclst = fs->free_clst;
+               } else {
+                       /* Scan FAT to obtain number of free clusters */
+                       nfree = 0;
+                       if (fs->fs_type == FS_FAT12) {  /* FAT12: Scan bit field FAT entries */
+                               clst = 2; obj.fs = fs;
+                               do {
+                                       stat = get_fat(&obj, clst);
+                                       if (stat == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }
+                                       if (stat == 1) { res = FR_INT_ERR; break; }
+                                       if (stat == 0) nfree++;
+                               } while (++clst < fs->n_fatent);
+                       } else {
+#if FF_FS_EXFAT
+                               if (fs->fs_type == FS_EXFAT) {  /* exFAT: Scan allocation bitmap */
+                                       BYTE bm;
+                                       UINT b;
+
+                                       clst = fs->n_fatent - 2;        /* Number of clusters */
+                                       sect = fs->bitbase;                     /* Bitmap sector */
+                                       i = 0;                                          /* Offset in the sector */
+                                       do {    /* Counts numbuer of bits with zero in the bitmap */
+                                               if (i == 0) {
+                                                       res = move_window(fs, sect++);
+                                                       if (res != FR_OK) break;
+                                               }
+                                               for (b = 8, bm = fs->win[i]; b && clst; b--, clst--) {
+                                                       if (!(bm & 1)) nfree++;
+                                                       bm >>= 1;
+                                               }
+                                               i = (i + 1) % SS(fs);
+                                       } while (clst);
+                               } else
+#endif
+                               {       /* FAT16/32: Scan WORD/DWORD FAT entries */
+                                       clst = fs->n_fatent;    /* Number of entries */
+                                       sect = fs->fatbase;             /* Top of the FAT */
+                                       i = 0;                                  /* Offset in the sector */
+                                       do {    /* Counts numbuer of entries with zero in the FAT */
+                                               if (i == 0) {
+                                                       res = move_window(fs, sect++);
+                                                       if (res != FR_OK) break;
+                                               }
+                                               if (fs->fs_type == FS_FAT16) {
+                                                       if (ld_word(fs->win + i) == 0) nfree++;
+                                                       i += 2;
+                                               } else {
+                                                       if ((ld_dword(fs->win + i) & 0x0FFFFFFF) == 0) nfree++;
+                                                       i += 4;
+                                               }
+                                               i %= SS(fs);
+                                       } while (--clst);
+                               }
+                       }
+                       *nclst = nfree;                 /* Return the free clusters */
+                       fs->free_clst = nfree;  /* Now free_clst is valid */
+                       fs->fsi_flag |= 1;              /* FAT32: FSInfo is to be updated */
+               }
+       }
+
+       LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Truncate File                                                         */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_truncate (
+       FIL* fp         /* Pointer to the file object */
+)
+{
+       FRESULT res;
+       FATFS *fs;
+       DWORD ncl;
+
+
+       res = validate(&fp->obj, &fs);  /* Check validity of the file object */
+       if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);
+       if (!(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED);    /* Check access mode */
+
+       if (fp->fptr < fp->obj.objsize) {       /* Process when fptr is not on the eof */
+               if (fp->fptr == 0) {    /* When set file size to zero, remove entire cluster chain */
+                       res = remove_chain(&fp->obj, fp->obj.sclust, 0);
+                       fp->obj.sclust = 0;
+               } else {                                /* When truncate a part of the file, remove remaining clusters */
+                       ncl = get_fat(&fp->obj, fp->clust);
+                       res = FR_OK;
+                       if (ncl == 0xFFFFFFFF) res = FR_DISK_ERR;
+                       if (ncl == 1) res = FR_INT_ERR;
+                       if (res == FR_OK && ncl < fs->n_fatent) {
+                               res = remove_chain(&fp->obj, ncl, fp->clust);
+                       }
+               }
+               fp->obj.objsize = fp->fptr;     /* Set file size to current read/write point */
+               fp->flag |= FA_MODIFIED;
+#if !FF_FS_TINY
+               if (res == FR_OK && (fp->flag & FA_DIRTY)) {
+                       if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) {
+                               res = FR_DISK_ERR;
+                       } else {
+                               fp->flag &= (BYTE)~FA_DIRTY;
+                       }
+               }
+#endif
+               if (res != FR_OK) ABORT(fs, res);
+       }
+
+       LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Delete a File/Directory                                               */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_unlink (
+       const TCHAR* path               /* Pointer to the file or directory path */
+)
+{
+       FRESULT res;
+       DIR dj, sdj;
+       DWORD dclst = 0;
+       FATFS *fs;
+#if FF_FS_EXFAT
+       FFOBJID obj;
+#endif
+       DEF_NAMBUF
+
+
+       /* Get logical drive */
+       res = mount_volume(&path, &fs, FA_WRITE);
+       if (res == FR_OK) {
+               dj.obj.fs = fs;
+               INIT_NAMBUF(fs);
+               res = follow_path(&dj, path);           /* Follow the file path */
+               if (FF_FS_RPATH && res == FR_OK && (dj.fn[NSFLAG] & NS_DOT)) {
+                       res = FR_INVALID_NAME;                  /* Cannot remove dot entry */
+               }
+#if FF_FS_LOCK != 0
+               if (res == FR_OK) res = chk_lock(&dj, 2);       /* Check if it is an open object */
+#endif
+               if (res == FR_OK) {                                     /* The object is accessible */
+                       if (dj.fn[NSFLAG] & NS_NONAME) {
+                               res = FR_INVALID_NAME;          /* Cannot remove the origin directory */
+                       } else {
+                               if (dj.obj.attr & AM_RDO) {
+                                       res = FR_DENIED;                /* Cannot remove R/O object */
+                               }
+                       }
+                       if (res == FR_OK) {
+#if FF_FS_EXFAT
+                               obj.fs = fs;
+                               if (fs->fs_type == FS_EXFAT) {
+                                       init_alloc_info(fs, &obj);
+                                       dclst = obj.sclust;
+                               } else
+#endif
+                               {
+                                       dclst = ld_clust(fs, dj.dir);
+                               }
+                               if (dj.obj.attr & AM_DIR) {                     /* Is it a sub-directory? */
+#if FF_FS_RPATH != 0
+                                       if (dclst == fs->cdir) {                        /* Is it the current directory? */
+                                               res = FR_DENIED;
+                                       } else
+#endif
+                                       {
+                                               sdj.obj.fs = fs;                                /* Open the sub-directory */
+                                               sdj.obj.sclust = dclst;
+#if FF_FS_EXFAT
+                                               if (fs->fs_type == FS_EXFAT) {
+                                                       sdj.obj.objsize = obj.objsize;
+                                                       sdj.obj.stat = obj.stat;
+                                               }
+#endif
+                                               res = dir_sdi(&sdj, 0);
+                                               if (res == FR_OK) {
+                                                       res = DIR_READ_FILE(&sdj);                      /* Test if the directory is empty */
+                                                       if (res == FR_OK) res = FR_DENIED;      /* Not empty? */
+                                                       if (res == FR_NO_FILE) res = FR_OK;     /* Empty? */
+                                               }
+                                       }
+                               }
+                       }
+                       if (res == FR_OK) {
+                               res = dir_remove(&dj);                  /* Remove the directory entry */
+                               if (res == FR_OK && dclst != 0) {       /* Remove the cluster chain if exist */
+#if FF_FS_EXFAT
+                                       res = remove_chain(&obj, dclst, 0);
+#else
+                                       res = remove_chain(&dj.obj, dclst, 0);
+#endif
+                               }
+                               if (res == FR_OK) res = sync_fs(fs);
+                       }
+               }
+               FREE_NAMBUF();
+       }
+
+       LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Create a Directory                                                    */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_mkdir (
+       const TCHAR* path               /* Pointer to the directory path */
+)
+{
+       FRESULT res;
+       DIR dj;
+       FFOBJID sobj;
+       FATFS *fs;
+       DWORD dcl, pcl, tm;
+       DEF_NAMBUF
+
+
+       res = mount_volume(&path, &fs, FA_WRITE);       /* Get logical drive */
+       if (res == FR_OK) {
+               dj.obj.fs = fs;
+               INIT_NAMBUF(fs);
+               res = follow_path(&dj, path);                   /* Follow the file path */
+               if (res == FR_OK) res = FR_EXIST;               /* Name collision? */
+               if (FF_FS_RPATH && res == FR_NO_FILE && (dj.fn[NSFLAG] & NS_DOT)) {     /* Invalid name? */
+                       res = FR_INVALID_NAME;
+               }
+               if (res == FR_NO_FILE) {                                /* It is clear to create a new directory */
+                       sobj.fs = fs;                                           /* New object id to create a new chain */
+                       dcl = create_chain(&sobj, 0);           /* Allocate a cluster for the new directory */
+                       res = FR_OK;
+                       if (dcl == 0) res = FR_DENIED;          /* No space to allocate a new cluster? */
+                       if (dcl == 1) res = FR_INT_ERR;         /* Any insanity? */
+                       if (dcl == 0xFFFFFFFF) res = FR_DISK_ERR;       /* Disk error? */
+                       tm = GET_FATTIME();
+                       if (res == FR_OK) {
+                               res = dir_clear(fs, dcl);               /* Clean up the new table */
+                               if (res == FR_OK) {
+                                       if (!FF_FS_EXFAT || fs->fs_type != FS_EXFAT) {  /* Create dot entries (FAT only) */
+                                               mem_set(fs->win + DIR_Name, ' ', 11);   /* Create "." entry */
+                                               fs->win[DIR_Name] = '.';
+                                               fs->win[DIR_Attr] = AM_DIR;
+                                               st_dword(fs->win + DIR_ModTime, tm);
+                                               st_clust(fs, fs->win, dcl);
+                                               mem_cpy(fs->win + SZDIRE, fs->win, SZDIRE); /* Create ".." entry */
+                                               fs->win[SZDIRE + 1] = '.'; pcl = dj.obj.sclust;
+                                               st_clust(fs, fs->win + SZDIRE, pcl);
+                                               fs->wflag = 1;
+                                       }
+                                       res = dir_register(&dj);        /* Register the object to the parent directoy */
+                               }
+                       }
+                       if (res == FR_OK) {
+#if FF_FS_EXFAT
+                               if (fs->fs_type == FS_EXFAT) {  /* Initialize directory entry block */
+                                       st_dword(fs->dirbuf + XDIR_ModTime, tm);        /* Created time */
+                                       st_dword(fs->dirbuf + XDIR_FstClus, dcl);       /* Table start cluster */
+                                       st_dword(fs->dirbuf + XDIR_FileSize, (DWORD)fs->csize * SS(fs));        /* Directory size needs to be valid */
+                                       st_dword(fs->dirbuf + XDIR_ValidFileSize, (DWORD)fs->csize * SS(fs));
+                                       fs->dirbuf[XDIR_GenFlags] = 3;                          /* Initialize the object flag */
+                                       fs->dirbuf[XDIR_Attr] = AM_DIR;                         /* Attribute */
+                                       res = store_xdir(&dj);
+                               } else
+#endif
+                               {
+                                       st_dword(dj.dir + DIR_ModTime, tm);     /* Created time */
+                                       st_clust(fs, dj.dir, dcl);                      /* Table start cluster */
+                                       dj.dir[DIR_Attr] = AM_DIR;                      /* Attribute */
+                                       fs->wflag = 1;
+                               }
+                               if (res == FR_OK) {
+                                       res = sync_fs(fs);
+                               }
+                       } else {
+                               remove_chain(&sobj, dcl, 0);            /* Could not register, remove the allocated cluster */
+                       }
+               }
+               FREE_NAMBUF();
+       }
+
+       LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Rename a File/Directory                                               */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_rename (
+       const TCHAR* path_old,  /* Pointer to the object name to be renamed */
+       const TCHAR* path_new   /* Pointer to the new name */
+)
+{
+       FRESULT res;
+       DIR djo, djn;
+       FATFS *fs;
+       BYTE buf[FF_FS_EXFAT ? SZDIRE * 2 : SZDIRE], *dir;
+       LBA_t sect;
+       DEF_NAMBUF
+
+
+       get_ldnumber(&path_new);                                                /* Snip the drive number of new name off */
+       res = mount_volume(&path_old, &fs, FA_WRITE);   /* Get logical drive of the old object */
+       if (res == FR_OK) {
+               djo.obj.fs = fs;
+               INIT_NAMBUF(fs);
+               res = follow_path(&djo, path_old);              /* Check old object */
+               if (res == FR_OK && (djo.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME;     /* Check validity of name */
+#if FF_FS_LOCK != 0
+               if (res == FR_OK) {
+                       res = chk_lock(&djo, 2);
+               }
+#endif
+               if (res == FR_OK) {                                             /* Object to be renamed is found */
+#if FF_FS_EXFAT
+                       if (fs->fs_type == FS_EXFAT) {  /* At exFAT volume */
+                               BYTE nf, nn;
+                               WORD nh;
+
+                               mem_cpy(buf, fs->dirbuf, SZDIRE * 2);   /* Save 85+C0 entry of old object */
+                               mem_cpy(&djn, &djo, sizeof djo);
+                               res = follow_path(&djn, path_new);              /* Make sure if new object name is not in use */
+                               if (res == FR_OK) {                                             /* Is new name already in use by any other object? */
+                                       res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST;
+                               }
+                               if (res == FR_NO_FILE) {                                /* It is a valid path and no name collision */
+                                       res = dir_register(&djn);                       /* Register the new entry */
+                                       if (res == FR_OK) {
+                                               nf = fs->dirbuf[XDIR_NumSec]; nn = fs->dirbuf[XDIR_NumName];
+                                               nh = ld_word(fs->dirbuf + XDIR_NameHash);
+                                               mem_cpy(fs->dirbuf, buf, SZDIRE * 2);   /* Restore 85+C0 entry */
+                                               fs->dirbuf[XDIR_NumSec] = nf; fs->dirbuf[XDIR_NumName] = nn;
+                                               st_word(fs->dirbuf + XDIR_NameHash, nh);
+                                               if (!(fs->dirbuf[XDIR_Attr] & AM_DIR)) fs->dirbuf[XDIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */
+/* Start of critical section where an interruption can cause a cross-link */
+                                               res = store_xdir(&djn);
+                                       }
+                               }
+                       } else
+#endif
+                       {       /* At FAT/FAT32 volume */
+                               mem_cpy(buf, djo.dir, SZDIRE);                  /* Save directory entry of the object */
+                               mem_cpy(&djn, &djo, sizeof (DIR));              /* Duplicate the directory object */
+                               res = follow_path(&djn, path_new);              /* Make sure if new object name is not in use */
+                               if (res == FR_OK) {                                             /* Is new name already in use by any other object? */
+                                       res = (djn.obj.sclust == djo.obj.sclust && djn.dptr == djo.dptr) ? FR_NO_FILE : FR_EXIST;
+                               }
+                               if (res == FR_NO_FILE) {                                /* It is a valid path and no name collision */
+                                       res = dir_register(&djn);                       /* Register the new entry */
+                                       if (res == FR_OK) {
+                                               dir = djn.dir;                                  /* Copy directory entry of the object except name */
+                                               mem_cpy(dir + 13, buf + 13, SZDIRE - 13);
+                                               dir[DIR_Attr] = buf[DIR_Attr];
+                                               if (!(dir[DIR_Attr] & AM_DIR)) dir[DIR_Attr] |= AM_ARC; /* Set archive attribute if it is a file */
+                                               fs->wflag = 1;
+                                               if ((dir[DIR_Attr] & AM_DIR) && djo.obj.sclust != djn.obj.sclust) {     /* Update .. entry in the sub-directory if needed */
+                                                       sect = clst2sect(fs, ld_clust(fs, dir));
+                                                       if (sect == 0) {
+                                                               res = FR_INT_ERR;
+                                                       } else {
+/* Start of critical section where an interruption can cause a cross-link */
+                                                               res = move_window(fs, sect);
+                                                               dir = fs->win + SZDIRE * 1;     /* Ptr to .. entry */
+                                                               if (res == FR_OK && dir[1] == '.') {
+                                                                       st_clust(fs, dir, djn.obj.sclust);
+                                                                       fs->wflag = 1;
+                                                               }
+                                                       }
+                                               }
+                                       }
+                               }
+                       }
+                       if (res == FR_OK) {
+                               res = dir_remove(&djo);         /* Remove old entry */
+                               if (res == FR_OK) {
+                                       res = sync_fs(fs);
+                               }
+                       }
+/* End of the critical section */
+               }
+               FREE_NAMBUF();
+       }
+
+       LEAVE_FF(fs, res);
+}
+
+#endif /* !FF_FS_READONLY */
+#endif /* FF_FS_MINIMIZE == 0 */
+#endif /* FF_FS_MINIMIZE <= 1 */
+#endif /* FF_FS_MINIMIZE <= 2 */
+
+
+
+#if FF_USE_CHMOD && !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Change Attribute                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_chmod (
+       const TCHAR* path,      /* Pointer to the file path */
+       BYTE attr,                      /* Attribute bits */
+       BYTE mask                       /* Attribute mask to change */
+)
+{
+       FRESULT res;
+       DIR dj;
+       FATFS *fs;
+       DEF_NAMBUF
+
+
+       res = mount_volume(&path, &fs, FA_WRITE);       /* Get logical drive */
+       if (res == FR_OK) {
+               dj.obj.fs = fs;
+               INIT_NAMBUF(fs);
+               res = follow_path(&dj, path);   /* Follow the file path */
+               if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME;      /* Check object validity */
+               if (res == FR_OK) {
+                       mask &= AM_RDO|AM_HID|AM_SYS|AM_ARC;    /* Valid attribute mask */
+#if FF_FS_EXFAT
+                       if (fs->fs_type == FS_EXFAT) {
+                               fs->dirbuf[XDIR_Attr] = (attr & mask) | (fs->dirbuf[XDIR_Attr] & (BYTE)~mask);  /* Apply attribute change */
+                               res = store_xdir(&dj);
+                       } else
+#endif
+                       {
+                               dj.dir[DIR_Attr] = (attr & mask) | (dj.dir[DIR_Attr] & (BYTE)~mask);    /* Apply attribute change */
+                               fs->wflag = 1;
+                       }
+                       if (res == FR_OK) {
+                               res = sync_fs(fs);
+                       }
+               }
+               FREE_NAMBUF();
+       }
+
+       LEAVE_FF(fs, res);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Change Timestamp                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_utime (
+       const TCHAR* path,      /* Pointer to the file/directory name */
+       const FILINFO* fno      /* Pointer to the timestamp to be set */
+)
+{
+       FRESULT res;
+       DIR dj;
+       FATFS *fs;
+       DEF_NAMBUF
+
+
+       res = mount_volume(&path, &fs, FA_WRITE);       /* Get logical drive */
+       if (res == FR_OK) {
+               dj.obj.fs = fs;
+               INIT_NAMBUF(fs);
+               res = follow_path(&dj, path);   /* Follow the file path */
+               if (res == FR_OK && (dj.fn[NSFLAG] & (NS_DOT | NS_NONAME))) res = FR_INVALID_NAME;      /* Check object validity */
+               if (res == FR_OK) {
+#if FF_FS_EXFAT
+                       if (fs->fs_type == FS_EXFAT) {
+                               st_dword(fs->dirbuf + XDIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime);
+                               res = store_xdir(&dj);
+                       } else
+#endif
+                       {
+                               st_dword(dj.dir + DIR_ModTime, (DWORD)fno->fdate << 16 | fno->ftime);
+                               fs->wflag = 1;
+                       }
+                       if (res == FR_OK) {
+                               res = sync_fs(fs);
+                       }
+               }
+               FREE_NAMBUF();
+       }
+
+       LEAVE_FF(fs, res);
+}
+
+#endif /* FF_USE_CHMOD && !FF_FS_READONLY */
+
+
+
+#if FF_USE_LABEL
+/*-----------------------------------------------------------------------*/
+/* Get Volume Label                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_getlabel (
+       const TCHAR* path,      /* Logical drive number */
+       TCHAR* label,           /* Buffer to store the volume label */
+       DWORD* vsn                      /* Variable to store the volume serial number */
+)
+{
+       FRESULT res;
+       DIR dj;
+       FATFS *fs;
+       UINT si, di;
+       WCHAR wc;
+
+       /* Get logical drive */
+       res = mount_volume(&path, &fs, 0);
+
+       /* Get volume label */
+       if (res == FR_OK && label) {
+               dj.obj.fs = fs; dj.obj.sclust = 0;      /* Open root directory */
+               res = dir_sdi(&dj, 0);
+               if (res == FR_OK) {
+                       res = DIR_READ_LABEL(&dj);              /* Find a volume label entry */
+                       if (res == FR_OK) {
+#if FF_FS_EXFAT
+                               if (fs->fs_type == FS_EXFAT) {
+                                       WCHAR hs;
+
+                                       for (si = di = hs = 0; si < dj.dir[XDIR_NumLabel]; si++) {      /* Extract volume label from 83 entry */
+                                               wc = ld_word(dj.dir + XDIR_Label + si * 2);
+                                               if (hs == 0 && IsSurrogate(wc)) {       /* Is the code a surrogate? */
+                                                       hs = wc; continue;
+                                               }
+                                               wc = put_utf((DWORD)hs << 16 | wc, &label[di], 4);
+                                               if (wc == 0) { di = 0; break; }
+                                               di += wc;
+                                               hs = 0;
+                                       }
+                                       if (hs != 0) di = 0;    /* Broken surrogate pair? */
+                                       label[di] = 0;
+                               } else
+#endif
+                               {
+                                       si = di = 0;            /* Extract volume label from AM_VOL entry */
+                                       while (si < 11) {
+                                               wc = dj.dir[si++];
+#if FF_USE_LFN && FF_LFN_UNICODE >= 1  /* Unicode output */
+                                               if (dbc_1st((BYTE)wc) && si < 11) wc = wc << 8 | dj.dir[si++];  /* Is it a DBC? */
+                                               wc = ff_oem2uni(wc, CODEPAGE);                                  /* Convert it into Unicode */
+                                               if (wc != 0) wc = put_utf(wc, &label[di], 4);   /* Put it in Unicode */
+                                               if (wc == 0) { di = 0; break; }
+                                               di += wc;
+#else                                                                  /* ANSI/OEM output */
+                                               label[di++] = (TCHAR)wc;
+#endif
+                                       }
+                                       do {                            /* Truncate trailing spaces */
+                                               label[di] = 0;
+                                               if (di == 0) break;
+                                       } while (label[--di] == ' ');
+                               }
+                       }
+               }
+               if (res == FR_NO_FILE) {        /* No label entry and return nul string */
+                       label[0] = 0;
+                       res = FR_OK;
+               }
+       }
+
+       /* Get volume serial number */
+       if (res == FR_OK && vsn) {
+               res = move_window(fs, fs->volbase);
+               if (res == FR_OK) {
+                       switch (fs->fs_type) {
+                       case FS_EXFAT:
+                               di = BPB_VolIDEx; break;
+
+                       case FS_FAT32:
+                               di = BS_VolID32; break;
+
+                       default:
+                               di = BS_VolID;
+                       }
+                       *vsn = ld_dword(fs->win + di);
+               }
+       }
+
+       LEAVE_FF(fs, res);
+}
+
+
+
+#if !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Set Volume Label                                                      */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_setlabel (
+       const TCHAR* label      /* Volume label to set with heading logical drive number */
+)
+{
+       FRESULT res;
+       DIR dj;
+       FATFS *fs;
+       BYTE dirvn[22];
+       UINT di;
+       WCHAR wc;
+       static const char badchr[] = "+.,;=[]/\\\"*:<>\?|\x7F"; /* [0..] for FAT, [7..] for exFAT */
+#if FF_USE_LFN
+       DWORD dc;
+#endif
+
+       /* Get logical drive */
+       res = mount_volume(&label, &fs, FA_WRITE);
+       if (res != FR_OK) LEAVE_FF(fs, res);
+
+#if FF_FS_EXFAT
+       if (fs->fs_type == FS_EXFAT) {  /* On the exFAT volume */
+               mem_set(dirvn, 0, 22);
+               di = 0;
+               while ((UINT)*label >= ' ') {   /* Create volume label */
+                       dc = tchar2uni(&label); /* Get a Unicode character */
+                       if (dc >= 0x10000) {
+                               if (dc == 0xFFFFFFFF || di >= 10) {     /* Wrong surrogate or buffer overflow */
+                                       dc = 0;
+                               } else {
+                                       st_word(dirvn + di * 2, (WCHAR)(dc >> 16)); di++;
+                               }
+                       }
+                       if (dc == 0 || chk_chr(badchr + 7, (int)dc) || di >= 11) {      /* Check validity of the volume label */
+                               LEAVE_FF(fs, FR_INVALID_NAME);
+                       }
+                       st_word(dirvn + di * 2, (WCHAR)dc); di++;
+               }
+       } else
+#endif
+       {       /* On the FAT/FAT32 volume */
+               mem_set(dirvn, ' ', 11);
+               di = 0;
+               while ((UINT)*label >= ' ') {   /* Create volume label */
+#if FF_USE_LFN
+                       dc = tchar2uni(&label);
+                       wc = (dc < 0x10000) ? ff_uni2oem(ff_wtoupper(dc), CODEPAGE) : 0;
+#else                                                                  /* ANSI/OEM input */
+                       wc = (BYTE)*label++;
+                       if (dbc_1st((BYTE)wc)) wc = dbc_2nd((BYTE)*label) ? wc << 8 | (BYTE)*label++ : 0;
+                       if (IsLower(wc)) wc -= 0x20;            /* To upper ASCII characters */
+#if FF_CODE_PAGE == 0
+                       if (ExCvt && wc >= 0x80) wc = ExCvt[wc - 0x80]; /* To upper extended characters (SBCS cfg) */
+#elif FF_CODE_PAGE < 900
+                       if (wc >= 0x80) wc = ExCvt[wc - 0x80];  /* To upper extended characters (SBCS cfg) */
+#endif
+#endif
+                       if (wc == 0 || chk_chr(badchr + 0, (int)wc) || di >= (UINT)((wc >= 0x100) ? 10 : 11)) { /* Reject invalid characters for volume label */
+                               LEAVE_FF(fs, FR_INVALID_NAME);
+                       }
+                       if (wc >= 0x100) dirvn[di++] = (BYTE)(wc >> 8);
+                       dirvn[di++] = (BYTE)wc;
+               }
+               if (dirvn[0] == DDEM) LEAVE_FF(fs, FR_INVALID_NAME);    /* Reject illegal name (heading DDEM) */
+               while (di && dirvn[di - 1] == ' ') di--;                                /* Snip trailing spaces */
+       }
+
+       /* Set volume label */
+       dj.obj.fs = fs; dj.obj.sclust = 0;      /* Open root directory */
+       res = dir_sdi(&dj, 0);
+       if (res == FR_OK) {
+               res = DIR_READ_LABEL(&dj);      /* Get volume label entry */
+               if (res == FR_OK) {
+                       if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) {
+                               dj.dir[XDIR_NumLabel] = (BYTE)di;       /* Change the volume label */
+                               mem_cpy(dj.dir + XDIR_Label, dirvn, 22);
+                       } else {
+                               if (di != 0) {
+                                       mem_cpy(dj.dir, dirvn, 11);     /* Change the volume label */
+                               } else {
+                                       dj.dir[DIR_Name] = DDEM;        /* Remove the volume label */
+                               }
+                       }
+                       fs->wflag = 1;
+                       res = sync_fs(fs);
+               } else {                        /* No volume label entry or an error */
+                       if (res == FR_NO_FILE) {
+                               res = FR_OK;
+                               if (di != 0) {  /* Create a volume label entry */
+                                       res = dir_alloc(&dj, 1);        /* Allocate an entry */
+                                       if (res == FR_OK) {
+                                               mem_set(dj.dir, 0, SZDIRE);     /* Clean the entry */
+                                               if (FF_FS_EXFAT && fs->fs_type == FS_EXFAT) {
+                                                       dj.dir[XDIR_Type] = ET_VLABEL;  /* Create volume label entry */
+                                                       dj.dir[XDIR_NumLabel] = (BYTE)di;
+                                                       mem_cpy(dj.dir + XDIR_Label, dirvn, 22);
+                                               } else {
+                                                       dj.dir[DIR_Attr] = AM_VOL;              /* Create volume label entry */
+                                                       mem_cpy(dj.dir, dirvn, 11);
+                                               }
+                                               fs->wflag = 1;
+                                               res = sync_fs(fs);
+                                       }
+                               }
+                       }
+               }
+       }
+
+       LEAVE_FF(fs, res);
+}
+
+#endif /* !FF_FS_READONLY */
+#endif /* FF_USE_LABEL */
+
+
+
+#if FF_USE_EXPAND && !FF_FS_READONLY
+/*-----------------------------------------------------------------------*/
+/* Allocate a Contiguous Blocks to the File                              */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_expand (
+       FIL* fp,                /* Pointer to the file object */
+       FSIZE_t fsz,    /* File size to be expanded to */
+       BYTE opt                /* Operation mode 0:Find and prepare or 1:Find and allocate */
+)
+{
+       FRESULT res;
+       FATFS *fs;
+       DWORD n, clst, stcl, scl, ncl, tcl, lclst;
+
+
+       res = validate(&fp->obj, &fs);          /* Check validity of the file object */
+       if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);
+       if (fsz == 0 || fp->obj.objsize != 0 || !(fp->flag & FA_WRITE)) LEAVE_FF(fs, FR_DENIED);
+#if FF_FS_EXFAT
+       if (fs->fs_type != FS_EXFAT && fsz >= 0x100000000) LEAVE_FF(fs, FR_DENIED);     /* Check if in size limit */
+#endif
+       n = (DWORD)fs->csize * SS(fs);  /* Cluster size */
+       tcl = (DWORD)(fsz / n) + ((fsz & (n - 1)) ? 1 : 0);     /* Number of clusters required */
+       stcl = fs->last_clst; lclst = 0;
+       if (stcl < 2 || stcl >= fs->n_fatent) stcl = 2;
+
+#if FF_FS_EXFAT
+       if (fs->fs_type == FS_EXFAT) {
+               scl = find_bitmap(fs, stcl, tcl);                       /* Find a contiguous cluster block */
+               if (scl == 0) res = FR_DENIED;                          /* No contiguous cluster block was found */
+               if (scl == 0xFFFFFFFF) res = FR_DISK_ERR;
+               if (res == FR_OK) {     /* A contiguous free area is found */
+                       if (opt) {              /* Allocate it now */
+                               res = change_bitmap(fs, scl, tcl, 1);   /* Mark the cluster block 'in use' */
+                               lclst = scl + tcl - 1;
+                       } else {                /* Set it as suggested point for next allocation */
+                               lclst = scl - 1;
+                       }
+               }
+       } else
+#endif
+       {
+               scl = clst = stcl; ncl = 0;
+               for (;;) {      /* Find a contiguous cluster block */
+                       n = get_fat(&fp->obj, clst);
+                       if (++clst >= fs->n_fatent) clst = 2;
+                       if (n == 1) { res = FR_INT_ERR; break; }
+                       if (n == 0xFFFFFFFF) { res = FR_DISK_ERR; break; }
+                       if (n == 0) {   /* Is it a free cluster? */
+                               if (++ncl == tcl) break;        /* Break if a contiguous cluster block is found */
+                       } else {
+                               scl = clst; ncl = 0;            /* Not a free cluster */
+                       }
+                       if (clst == stcl) { res = FR_DENIED; break; }   /* No contiguous cluster? */
+               }
+               if (res == FR_OK) {     /* A contiguous free area is found */
+                       if (opt) {              /* Allocate it now */
+                               for (clst = scl, n = tcl; n; clst++, n--) {     /* Create a cluster chain on the FAT */
+                                       res = put_fat(fs, clst, (n == 1) ? 0xFFFFFFFF : clst + 1);
+                                       if (res != FR_OK) break;
+                                       lclst = clst;
+                               }
+                       } else {                /* Set it as suggested point for next allocation */
+                               lclst = scl - 1;
+                       }
+               }
+       }
+
+       if (res == FR_OK) {
+               fs->last_clst = lclst;          /* Set suggested start cluster to start next */
+               if (opt) {      /* Is it allocated now? */
+                       fp->obj.sclust = scl;           /* Update object allocation information */
+                       fp->obj.objsize = fsz;
+                       if (FF_FS_EXFAT) fp->obj.stat = 2;      /* Set status 'contiguous chain' */
+                       fp->flag |= FA_MODIFIED;
+                       if (fs->free_clst <= fs->n_fatent - 2) {        /* Update FSINFO */
+                               fs->free_clst -= tcl;
+                               fs->fsi_flag |= 1;
+                       }
+               }
+       }
+
+       LEAVE_FF(fs, res);
+}
+
+#endif /* FF_USE_EXPAND && !FF_FS_READONLY */
+
+
+
+#if FF_USE_FORWARD
+/*-----------------------------------------------------------------------*/
+/* Forward Data to the Stream Directly                                   */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_forward (
+       FIL* fp,                                                /* Pointer to the file object */
+       UINT (*func)(const BYTE*,UINT), /* Pointer to the streaming function */
+       UINT btf,                                               /* Number of bytes to forward */
+       UINT* bf                                                /* Pointer to number of bytes forwarded */
+)
+{
+       FRESULT res;
+       FATFS *fs;
+       DWORD clst;
+       LBA_t sect;
+       FSIZE_t remain;
+       UINT rcnt, csect;
+       BYTE *dbuf;
+
+
+       *bf = 0;        /* Clear transfer byte counter */
+       res = validate(&fp->obj, &fs);          /* Check validity of the file object */
+       if (res != FR_OK || (res = (FRESULT)fp->err) != FR_OK) LEAVE_FF(fs, res);
+       if (!(fp->flag & FA_READ)) LEAVE_FF(fs, FR_DENIED);     /* Check access mode */
+
+       remain = fp->obj.objsize - fp->fptr;
+       if (btf > remain) btf = (UINT)remain;                   /* Truncate btf by remaining bytes */
+
+       for ( ;  btf && (*func)(0, 0);                                  /* Repeat until all data transferred or stream goes busy */
+               fp->fptr += rcnt, *bf += rcnt, btf -= rcnt) {
+               csect = (UINT)(fp->fptr / SS(fs) & (fs->csize - 1));    /* Sector offset in the cluster */
+               if (fp->fptr % SS(fs) == 0) {                           /* On the sector boundary? */
+                       if (csect == 0) {                                               /* On the cluster boundary? */
+                               clst = (fp->fptr == 0) ?                        /* On the top of the file? */
+                                       fp->obj.sclust : get_fat(&fp->obj, fp->clust);
+                               if (clst <= 1) ABORT(fs, FR_INT_ERR);
+                               if (clst == 0xFFFFFFFF) ABORT(fs, FR_DISK_ERR);
+                               fp->clust = clst;                                       /* Update current cluster */
+                       }
+               }
+               sect = clst2sect(fs, fp->clust);                        /* Get current data sector */
+               if (sect == 0) ABORT(fs, FR_INT_ERR);
+               sect += csect;
+#if FF_FS_TINY
+               if (move_window(fs, sect) != FR_OK) ABORT(fs, FR_DISK_ERR);     /* Move sector window to the file data */
+               dbuf = fs->win;
+#else
+               if (fp->sect != sect) {         /* Fill sector cache with file data */
+#if !FF_FS_READONLY
+                       if (fp->flag & FA_DIRTY) {              /* Write-back dirty sector cache */
+                               if (disk_write(fs->pdrv, fp->buf, fp->sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
+                               fp->flag &= (BYTE)~FA_DIRTY;
+                       }
+#endif
+                       if (disk_read(fs->pdrv, fp->buf, sect, 1) != RES_OK) ABORT(fs, FR_DISK_ERR);
+               }
+               dbuf = fp->buf;
+#endif
+               fp->sect = sect;
+               rcnt = SS(fs) - (UINT)fp->fptr % SS(fs);        /* Number of bytes remains in the sector */
+               if (rcnt > btf) rcnt = btf;                                     /* Clip it by btr if needed */
+               rcnt = (*func)(dbuf + ((UINT)fp->fptr % SS(fs)), rcnt); /* Forward the file data */
+               if (rcnt == 0) ABORT(fs, FR_INT_ERR);
+       }
+
+       LEAVE_FF(fs, FR_OK);
+}
+#endif /* FF_USE_FORWARD */
+
+
+
+#if !FF_FS_READONLY && FF_USE_MKFS
+/*-----------------------------------------------------------------------*/
+/* Create an FAT/exFAT volume                                            */
+/*-----------------------------------------------------------------------*/
+
+#define N_SEC_TRACK 63                 /* Sectors per track for determination of drive CHS */
+#define        GPT_ALIGN       0x100000        /* Alignment of partitions in GPT [byte] (>=128KB) */
+#define GPT_ITEMS      128                     /* Number of GPT table size (>=128, sector aligned) */
+
+
+/* Create partitions on the physical drive */
+
+static FRESULT create_partition (
+       BYTE drv,                       /* Physical drive number */
+       const LBA_t plst[],     /* Partition list */
+       UINT sys,                       /* System ID (for only MBR, temp setting) and bit8:GPT */
+       BYTE* buf                       /* Working buffer for a sector */
+)
+{
+       UINT i, cy;
+       LBA_t sz_drv;
+       DWORD sz_drv32, s_lba32, n_lba32;
+       BYTE *pte, hd, n_hd, sc, n_sc;
+
+       /* Get drive size */
+       if (disk_ioctl(drv, GET_SECTOR_COUNT, &sz_drv) != RES_OK) return FR_DISK_ERR;
+
+#if FF_LBA64
+       if (sz_drv >= FF_MIN_GPT) {     /* Create partitions in GPT */
+               WORD ss;
+               UINT sz_pt, pi, si, ofs;
+               DWORD bcc, rnd, align;
+               QWORD s_lba64, n_lba64, sz_pool, s_bpt;
+               static const BYTE gpt_mbr[16] = {0x00, 0x00, 0x02, 0x00, 0xEE, 0xFE, 0xFF, 0x00, 0x01, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
+
+#if FF_MAX_SS != FF_MIN_SS
+               if (disk_ioctl(drv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR;        /* Get sector size */
+               if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR;
+#else
+               ss = FF_MAX_SS;
+#endif
+               rnd = GET_FATTIME();                    /* Random seed */
+               align = GPT_ALIGN / ss;                 /* Partition alignment [sector] */
+               sz_pt = GPT_ITEMS * SZ_GPTE / ss;       /* Size of PT [sector] */
+               s_bpt = sz_drv - sz_pt - 1;             /* Backup PT start sector */
+               s_lba64 = 2 + sz_pt;                    /* First allocatable sector */
+               sz_pool = s_bpt - s_lba64;              /* Size of allocatable area */
+               bcc = 0xFFFFFFFF; n_lba64 = 1;
+               pi = si = 0;    /* partition table index, size table index */
+               do {
+                       if (pi * SZ_GPTE % ss == 0) mem_set(buf, 0, ss);        /* Clean the buffer if needed */
+                       if (n_lba64 != 0) {     /* Is the size table not termintated? */
+                               s_lba64 = (s_lba64 + align - 1) & ((QWORD)0 - align);   /* Align partition start */
+                               n_lba64 = plst[si++];   /* Get a partition size */
+                               if (n_lba64 <= 100) {   /* Is the size in percentage? */
+                                       n_lba64 = sz_pool * n_lba64 / 100;
+                                       n_lba64 = (n_lba64 + align - 1) & ((QWORD)0 - align);   /* Align partition end (only if in percentage) */
+                               }
+                               if (s_lba64 + n_lba64 > s_bpt) {        /* Clip at end of the pool */
+                                       n_lba64 = (s_lba64 < s_bpt) ? s_bpt - s_lba64 : 0;
+                               }
+                       }
+                       if (n_lba64 != 0) {             /* Add a partition? */
+                               ofs = pi * SZ_GPTE % ss;
+                               mem_cpy(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, 16);    /* Partition GUID (Microsoft Basic Data) */
+                               rnd = make_rand(rnd, buf + ofs + GPTE_UpGuid, 16);              /* Unique partition GUID */
+                               st_qword(buf + ofs + GPTE_FstLba, s_lba64);                             /* Partition start LBA */
+                               st_qword(buf + ofs + GPTE_LstLba, s_lba64 + n_lba64 - 1);       /* Partition end LBA */
+                               s_lba64 += n_lba64;             /* Next partition LBA */
+                       }
+                       if ((pi + 1) * SZ_GPTE % ss == 0) {             /* Write the buffer if it is filled up */
+                               for (i = 0; i < ss; bcc = crc32(bcc, buf[i++])) ;       /* Calculate table check sum */
+                               if (disk_write(drv, buf, 2 + pi * SZ_GPTE / ss, 1) != RES_OK) return FR_DISK_ERR;               /* Primary table */
+                               if (disk_write(drv, buf, s_bpt + pi * SZ_GPTE / ss, 1) != RES_OK) return FR_DISK_ERR;   /* Secondary table */
+                       }
+               } while (++pi < GPT_ITEMS);
+
+               /* Create primary GPT header */
+               mem_set(buf, 0, ss);
+               mem_cpy(buf + GPTH_Sign, "EFI PART" "\0\0\1\0" "\x5C\0\0", 16); /* Signature, version (1.0) and size (92) */
+               st_dword(buf + GPTH_PtBcc, ~bcc);                               /* Table check sum */
+               st_qword(buf + GPTH_CurLba, 1);                                 /* LBA of this header */
+               st_qword(buf + GPTH_BakLba, sz_drv - 1);                /* LBA of another header */
+               st_qword(buf + GPTH_FstLba, 2 + sz_pt);                 /* LBA of first allocatable sector */
+               st_qword(buf + GPTH_LstLba, s_bpt - 1);                 /* LBA of last allocatable sector */
+               st_dword(buf + GPTH_PteSize, SZ_GPTE);                  /* Size of a table entry */
+               st_dword(buf + GPTH_PtNum, GPT_ITEMS);                  /* Number of table entries */
+               st_dword(buf + GPTH_PtOfs, 2);                                  /* LBA of this table */
+               rnd = make_rand(rnd, buf + GPTH_DskGuid, 16);   /* Disk GUID */
+               for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, buf[i++])) ;      /* Calculate header check sum */
+               st_dword(buf + GPTH_Bcc, ~bcc);                                 /* Header check sum */
+               if (disk_write(drv, buf, 1, 1) != RES_OK) return FR_DISK_ERR;
+
+               /* Create secondary GPT header */
+               st_qword(buf + GPTH_CurLba, sz_drv - 1);                /* LBA of this header */
+               st_qword(buf + GPTH_BakLba, 1);                                 /* LBA of another header */
+               st_qword(buf + GPTH_PtOfs, s_bpt);                              /* LBA of this table */
+               st_dword(buf + GPTH_Bcc, 0);
+               for (i = 0, bcc= 0xFFFFFFFF; i < 92; bcc = crc32(bcc, buf[i++])) ;      /* Calculate header check sum */
+               st_dword(buf + GPTH_Bcc, ~bcc);                                 /* Header check sum */
+               if (disk_write(drv, buf, sz_drv - 1, 1) != RES_OK) return FR_DISK_ERR;
+
+               /* Create protective MBR */
+               mem_set(buf, 0, ss);
+               mem_cpy(buf + MBR_Table, gpt_mbr, 16);                  /* Create a GPT partition */
+               st_word(buf + BS_55AA, 0xAA55);
+               if (disk_write(drv, buf, 0, 1) != RES_OK) return FR_DISK_ERR;
+
+       } else
+#endif
+       {                                       /* Create partitions in MBR */
+               sz_drv32 = (DWORD)sz_drv;
+               n_sc = N_SEC_TRACK;             /* Determine drive CHS without any consideration of the drive geometry */
+               for (n_hd = 8; n_hd != 0 && sz_drv32 / n_hd / n_sc > 1024; n_hd *= 2) ;
+               if (n_hd == 0) n_hd = 255;      /* Number of heads needs to be <256 */
+
+               mem_set(buf, 0, FF_MAX_SS);     /* Clear MBR */
+               pte = buf + MBR_Table;  /* Partition table in the MBR */
+               for (i = 0, s_lba32 = 2048; i < 4 && s_lba32 != 0 && s_lba32 < sz_drv32; i++, s_lba32 += n_lba32) {
+                       n_lba32 = (DWORD)plst[i];       /* Get partition size */
+                       if (n_lba32 <= 100) n_lba32 = (n_lba32 == 100) ? sz_drv32 : sz_drv32 / 100 * n_lba32;   /* Size in percentage? */
+                       if (s_lba32 + n_lba32 > sz_drv32 || s_lba32 + n_lba32 < s_lba32) n_lba32 = sz_drv32 - s_lba32;  /* Clip at drive size */
+                       if (n_lba32 == 0) break;        /* End of table or no sector to allocate? */
+
+                       st_dword(pte + PTE_StLba, s_lba32);             /* Start LBA */
+                       st_dword(pte + PTE_SizLba, n_lba32);    /* Number of sectors */
+                       pte[PTE_System] = (BYTE)sys;                    /* System type */
+
+                       cy = (UINT)(s_lba32 / n_sc / n_hd);             /* Start cylinder */
+                       hd = (BYTE)(s_lba32 / n_sc % n_hd);             /* Start head */
+                       sc = (BYTE)(s_lba32 % n_sc + 1);                /* Start sector */
+                       pte[PTE_StHead] = hd;
+                       pte[PTE_StSec] = (BYTE)((cy >> 2 & 0xC0) | sc);
+                       pte[PTE_StCyl] = (BYTE)cy;
+
+                       cy = (UINT)((s_lba32 + n_lba32 - 1) / n_sc / n_hd);     /* End cylinder */
+                       hd = (BYTE)((s_lba32 + n_lba32 - 1) / n_sc % n_hd);     /* End head */
+                       sc = (BYTE)((s_lba32 + n_lba32 - 1) % n_sc + 1);        /* End sector */
+                       pte[PTE_EdHead] = hd;
+                       pte[PTE_EdSec] = (BYTE)((cy >> 2 & 0xC0) | sc);
+                       pte[PTE_EdCyl] = (BYTE)cy;
+
+                       pte += SZ_PTE;          /* Next entry */
+               }
+
+               st_word(buf + BS_55AA, 0xAA55);         /* MBR signature */
+               if (disk_write(drv, buf, 0, 1) != RES_OK) return FR_DISK_ERR;   /* Write it to the MBR */
+       }
+
+       return FR_OK;
+}
+
+
+
+FRESULT f_mkfs (
+       const TCHAR* path,              /* Logical drive number */
+       const MKFS_PARM* opt,   /* Format options */
+       void* work,                             /* Pointer to working buffer (null: use heap memory) */
+       UINT len                                /* Size of working buffer [byte] */
+)
+{
+       static const WORD cst[] = {1, 4, 16, 64, 256, 512, 0};  /* Cluster size boundary for FAT volume (4Ks unit) */
+       static const WORD cst32[] = {1, 2, 4, 8, 16, 32, 0};    /* Cluster size boundary for FAT32 volume (128Ks unit) */
+       static const MKFS_PARM defopt = {FM_ANY, 0, 0, 0, 0};   /* Default parameter */
+       BYTE fsopt, fsty, sys, *buf, *pte, pdrv, ipart;
+       WORD ss;        /* Sector size */
+       DWORD sz_buf, sz_blk, n_clst, pau, nsect, n;
+       LBA_t sz_vol, b_vol, b_fat, b_data;             /* Size of volume, Base LBA of volume, fat, data */
+       LBA_t sect, lba[2];
+       DWORD sz_rsv, sz_fat, sz_dir, sz_au;    /* Size of reserved, fat, dir, data, cluster */
+       UINT n_fat, n_root, i;                                  /* Index, Number of FATs and Number of roor dir entries */
+       int vol;
+       DSTATUS ds;
+       FRESULT fr;
+
+
+       /* Check mounted drive and clear work area */
+       vol = get_ldnumber(&path);                                      /* Get target logical drive */
+       if (vol < 0) return FR_INVALID_DRIVE;
+       if (FatFs[vol]) FatFs[vol]->fs_type = 0;        /* Clear the fs object if mounted */
+       pdrv = LD2PD(vol);                      /* Physical drive */
+       ipart = LD2PT(vol);                     /* Partition (0:create as new, 1..:get from partition table) */
+       if (!opt) opt = &defopt;        /* Use default parameter if it is not given */
+
+       /* Get physical drive status (sz_drv, sz_blk, ss) */
+       ds = disk_initialize(pdrv);
+       if (ds & STA_NOINIT) return FR_NOT_READY;
+       if (ds & STA_PROTECT) return FR_WRITE_PROTECTED;
+       sz_blk = opt->align;
+       if (sz_blk == 0 && disk_ioctl(pdrv, GET_BLOCK_SIZE, &sz_blk) != RES_OK) sz_blk = 1;
+       if (sz_blk == 0 || sz_blk > 0x8000 || (sz_blk & (sz_blk - 1))) sz_blk = 1;
+#if FF_MAX_SS != FF_MIN_SS
+       if (disk_ioctl(pdrv, GET_SECTOR_SIZE, &ss) != RES_OK) return FR_DISK_ERR;
+       if (ss > FF_MAX_SS || ss < FF_MIN_SS || (ss & (ss - 1))) return FR_DISK_ERR;
+#else
+       ss = FF_MAX_SS;
+#endif
+       /* Options for FAT sub-type and FAT parameters */
+       fsopt = opt->fmt & (FM_ANY | FM_SFD);
+       n_fat = (opt->n_fat >= 1 && opt->n_fat <= 2) ? opt->n_fat : 1;
+       n_root = (opt->n_root >= 1 && opt->n_root <= 32768 && (opt->n_root % (ss / SZDIRE)) == 0) ? opt->n_root : 512;
+       sz_au = (opt->au_size <= 0x1000000 && (opt->au_size & (opt->au_size - 1)) == 0) ? opt->au_size : 0;
+       sz_au /= ss;    /* Byte --> Sector */
+
+       /* Get working buffer */
+       sz_buf = len / ss;              /* Size of working buffer [sector] */
+       if (sz_buf == 0) return FR_NOT_ENOUGH_CORE;
+       buf = (BYTE*)work;              /* Working buffer */
+#if FF_USE_LFN == 3
+       if (!buf) buf = ff_memalloc(sz_buf * ss);       /* Use heap memory for working buffer */
+#endif
+       if (!buf) return FR_NOT_ENOUGH_CORE;
+
+       /* Determine where the volume to be located (b_vol, sz_vol) */
+       b_vol = sz_vol = 0;
+       if (FF_MULTI_PARTITION && ipart != 0) { /* Is the volume associated with any specific partition? */
+               /* Get partition location from the existing partition table */
+               if (disk_read(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);      /* Load MBR */
+               if (ld_word(buf + BS_55AA) != 0xAA55) LEAVE_MKFS(FR_MKFS_ABORTED);      /* Check if MBR is valid */
+#if FF_LBA64
+               if (buf[MBR_Table + PTE_System] == 0xEE) {      /* GPT protective MBR? */
+                       DWORD n_ent, ofs;
+                       QWORD pt_lba;
+
+                       /* Get the partition location from GPT */
+                       if (disk_read(pdrv, buf, 1, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);      /* Load GPT header sector (next to MBR) */
+                       if (!test_gpt_header(buf)) LEAVE_MKFS(FR_MKFS_ABORTED); /* Check if GPT header is valid */
+                       n_ent = ld_dword(buf + GPTH_PtNum);             /* Number of entries */
+                       pt_lba = ld_qword(buf + GPTH_PtOfs);    /* Table start sector */
+                       ofs = i = 0;
+                       while (n_ent) {         /* Find MS Basic partition with order of ipart */
+                               if (ofs == 0 && disk_read(pdrv, buf, pt_lba++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);   /* Get PT sector */
+                               if (!mem_cmp(buf + ofs + GPTE_PtGuid, GUID_MS_Basic, 16) && ++i == ipart) {     /* MS basic data partition? */
+                                       b_vol = ld_qword(buf + ofs + GPTE_FstLba);
+                                       sz_vol = ld_qword(buf + ofs + GPTE_LstLba) - b_vol + 1;
+                                       break;
+                               }
+                               n_ent--; ofs = (ofs + SZ_GPTE) % ss;    /* Next entry */
+                       }
+                       if (n_ent == 0) LEAVE_MKFS(FR_MKFS_ABORTED);    /* Partition not found */
+                       fsopt |= 0x80;  /* Partitioning is in GPT */
+               } else
+#endif
+               {       /* Get the partition location from MBR partition table */
+                       pte = buf + (MBR_Table + (ipart - 1) * SZ_PTE);
+                       if (ipart > 4 || pte[PTE_System] == 0) LEAVE_MKFS(FR_MKFS_ABORTED);     /* No partition? */
+                       b_vol = ld_dword(pte + PTE_StLba);              /* Get volume start sector */
+                       sz_vol = ld_dword(pte + PTE_SizLba);    /* Get volume size */
+               }
+       } else {        /* The volume is associated with a physical drive */
+               if (disk_ioctl(pdrv, GET_SECTOR_COUNT, &sz_vol) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+               if (!(fsopt & FM_SFD)) {        /* To be partitioned? */
+                       /* Create a single-partition on the drive in this function */
+#if FF_LBA64
+                       if (sz_vol >= FF_MIN_GPT) {     /* Which partition type to create, MBR or GPT? */
+                               fsopt |= 0x80;          /* Partitioning is in GPT */
+                               b_vol = GPT_ALIGN / ss; sz_vol -= b_vol + GPT_ITEMS * SZ_GPTE / ss + 1; /* Estimated partition offset and size */
+                       } else
+#endif
+                       {       /* Partitioning is in MBR */
+                               if (sz_vol > N_SEC_TRACK) {
+                                       b_vol = 2048; sz_vol -= b_vol;  /* Estimated partition offset and size */
+                               }
+                       }
+               }
+       }
+       if (sz_vol < 128) LEAVE_MKFS(FR_MKFS_ABORTED);  /* Check if volume size is >=128s */
+
+       /* Now start to create a FAT volume at b_vol and sz_vol */
+
+       do {    /* Pre-determine the FAT type */
+               if (FF_FS_EXFAT && (fsopt & FM_EXFAT)) {        /* exFAT possible? */
+                       if ((fsopt & FM_ANY) == FM_EXFAT || sz_vol >= 0x4000000 || sz_au > 128) {       /* exFAT only, vol >= 64MS or sz_au > 128S ? */
+                               fsty = FS_EXFAT; break;
+                       }
+               }
+#if FF_LBA64
+               if (sz_vol >= 0x100000000) LEAVE_MKFS(FR_MKFS_ABORTED); /* Too large volume for FAT/FAT32 */
+#endif
+               if (sz_au > 128) sz_au = 128;   /* Invalid AU for FAT/FAT32? */
+               if (fsopt & FM_FAT32) { /* FAT32 possible? */
+                       if (!(fsopt & FM_FAT)) {        /* no-FAT? */
+                               fsty = FS_FAT32; break;
+                       }
+               }
+               if (!(fsopt & FM_FAT)) LEAVE_MKFS(FR_INVALID_PARAMETER);        /* no-FAT? */
+               fsty = FS_FAT16;
+       } while (0);
+
+#if FF_FS_EXFAT
+       if (fsty == FS_EXFAT) { /* Create an exFAT volume */
+               DWORD szb_bit, szb_case, sum, nb, cl, tbl[3];
+               WCHAR ch, si;
+               UINT j, st;
+               BYTE b;
+
+               if (sz_vol < 0x1000) LEAVE_MKFS(FR_MKFS_ABORTED);       /* Too small volume for exFAT? */
+#if FF_USE_TRIM
+               lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1;    /* Inform storage device that the volume area may be erased */
+               disk_ioctl(pdrv, CTRL_TRIM, lba);
+#endif
+               /* Determine FAT location, data location and number of clusters */
+               if (sz_au == 0) {       /* AU auto-selection */
+                       sz_au = 8;
+                       if (sz_vol >= 0x80000) sz_au = 64;              /* >= 512Ks */
+                       if (sz_vol >= 0x4000000) sz_au = 256;   /* >= 64Ms */
+               }
+               b_fat = b_vol + 32;                                                                             /* FAT start at offset 32 */
+               sz_fat = (DWORD)((sz_vol / sz_au + 2) * 4 + ss - 1) / ss;       /* Number of FAT sectors */
+               b_data = (b_fat + sz_fat + sz_blk - 1) & ~((LBA_t)sz_blk - 1);  /* Align data area to the erase block boundary */
+               if (b_data - b_vol >= sz_vol / 2) LEAVE_MKFS(FR_MKFS_ABORTED);  /* Too small volume? */
+               n_clst = (DWORD)(sz_vol - (b_data - b_vol)) / sz_au;    /* Number of clusters */
+               if (n_clst <16) LEAVE_MKFS(FR_MKFS_ABORTED);                    /* Too few clusters? */
+               if (n_clst > MAX_EXFAT) LEAVE_MKFS(FR_MKFS_ABORTED);    /* Too many clusters? */
+
+               szb_bit = (n_clst + 7) / 8;                                                     /* Size of allocation bitmap */
+               tbl[0] = (szb_bit + sz_au * ss - 1) / (sz_au * ss);     /* Number of allocation bitmap clusters */
+
+               /* Create a compressed up-case table */
+               sect = b_data + sz_au * tbl[0]; /* Table start sector */
+               sum = 0;                                                /* Table checksum to be stored in the 82 entry */
+               st = 0; si = 0; i = 0; j = 0; szb_case = 0;
+               do {
+                       switch (st) {
+                       case 0:
+                               ch = (WCHAR)ff_wtoupper(si);    /* Get an up-case char */
+                               if (ch != si) {
+                                       si++; break;            /* Store the up-case char if exist */
+                               }
+                               for (j = 1; (WCHAR)(si + j) && (WCHAR)(si + j) == ff_wtoupper((WCHAR)(si + j)); j++) ;  /* Get run length of no-case block */
+                               if (j >= 128) {
+                                       ch = 0xFFFF; st = 2; break;     /* Compress the no-case block if run is >= 128 */
+                               }
+                               st = 1;                 /* Do not compress short run */
+                               /* go to next case */
+                       case 1:
+                               ch = si++;              /* Fill the short run */
+                               if (--j == 0) st = 0;
+                               break;
+
+                       default:
+                               ch = (WCHAR)j; si += (WCHAR)j;  /* Number of chars to skip */
+                               st = 0;
+                       }
+                       sum = xsum32(buf[i + 0] = (BYTE)ch, sum);               /* Put it into the write buffer */
+                       sum = xsum32(buf[i + 1] = (BYTE)(ch >> 8), sum);
+                       i += 2; szb_case += 2;
+                       if (si == 0 || i == sz_buf * ss) {              /* Write buffered data when buffer full or end of process */
+                               n = (i + ss - 1) / ss;
+                               if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+                               sect += n; i = 0;
+                       }
+               } while (si);
+               tbl[1] = (szb_case + sz_au * ss - 1) / (sz_au * ss);    /* Number of up-case table clusters */
+               tbl[2] = 1;                                                                             /* Number of root dir clusters */
+
+               /* Initialize the allocation bitmap */
+               sect = b_data; nsect = (szb_bit + ss - 1) / ss; /* Start of bitmap and number of sectors */
+               nb = tbl[0] + tbl[1] + tbl[2];                                  /* Number of clusters in-use by system */
+               do {
+                       mem_set(buf, 0, sz_buf * ss);
+                       for (i = 0; nb >= 8 && i < sz_buf * ss; buf[i++] = 0xFF, nb -= 8) ;
+                       for (b = 1; nb != 0 && i < sz_buf * ss; buf[i] |= b, b <<= 1, nb--) ;
+                       n = (nsect > sz_buf) ? sz_buf : nsect;          /* Write the buffered data */
+                       if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+                       sect += n; nsect -= n;
+               } while (nsect);
+
+               /* Initialize the FAT */
+               sect = b_fat; nsect = sz_fat;   /* Start of FAT and number of FAT sectors */
+               j = nb = cl = 0;
+               do {
+                       mem_set(buf, 0, sz_buf * ss); i = 0;    /* Clear work area and reset write index */
+                       if (cl == 0) {  /* Set FAT [0] and FAT[1] */
+                               st_dword(buf + i, 0xFFFFFFF8); i += 4; cl++;
+                               st_dword(buf + i, 0xFFFFFFFF); i += 4; cl++;
+                       }
+                       do {                    /* Create chains of bitmap, up-case and root dir */
+                               while (nb != 0 && i < sz_buf * ss) {    /* Create a chain */
+                                       st_dword(buf + i, (nb > 1) ? cl + 1 : 0xFFFFFFFF);
+                                       i += 4; cl++; nb--;
+                               }
+                               if (nb == 0 && j < 3) nb = tbl[j++];    /* Next chain */
+                       } while (nb != 0 && i < sz_buf * ss);
+                       n = (nsect > sz_buf) ? sz_buf : nsect;  /* Write the buffered data */
+                       if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+                       sect += n; nsect -= n;
+               } while (nsect);
+
+               /* Initialize the root directory */
+               mem_set(buf, 0, sz_buf * ss);
+               buf[SZDIRE * 0 + 0] = ET_VLABEL;                                        /* Volume label entry (no label) */
+               buf[SZDIRE * 1 + 0] = ET_BITMAP;                                        /* Bitmap entry */
+               st_dword(buf + SZDIRE * 1 + 20, 2);                                     /*  cluster */
+               st_dword(buf + SZDIRE * 1 + 24, szb_bit);                       /*  size */
+               buf[SZDIRE * 2 + 0] = ET_UPCASE;                                        /* Up-case table entry */
+               st_dword(buf + SZDIRE * 2 + 4, sum);                            /*  sum */
+               st_dword(buf + SZDIRE * 2 + 20, 2 + tbl[0]);            /*  cluster */
+               st_dword(buf + SZDIRE * 2 + 24, szb_case);                      /*  size */
+               sect = b_data + sz_au * (tbl[0] + tbl[1]); nsect = sz_au;       /* Start of the root directory and number of sectors */
+               do {    /* Fill root directory sectors */
+                       n = (nsect > sz_buf) ? sz_buf : nsect;
+                       if (disk_write(pdrv, buf, sect, n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+                       mem_set(buf, 0, ss);
+                       sect += n; nsect -= n;
+               } while (nsect);
+
+               /* Create two set of the exFAT VBR blocks */
+               sect = b_vol;
+               for (n = 0; n < 2; n++) {
+                       /* Main record (+0) */
+                       mem_set(buf, 0, ss);
+                       mem_cpy(buf + BS_JmpBoot, "\xEB\x76\x90" "EXFAT   ", 11);       /* Boot jump code (x86), OEM name */
+                       st_qword(buf + BPB_VolOfsEx, b_vol);                                    /* Volume offset in the physical drive [sector] */
+                       st_qword(buf + BPB_TotSecEx, sz_vol);                                   /* Volume size [sector] */
+                       st_dword(buf + BPB_FatOfsEx, (DWORD)(b_fat - b_vol));   /* FAT offset [sector] */
+                       st_dword(buf + BPB_FatSzEx, sz_fat);                                    /* FAT size [sector] */
+                       st_dword(buf + BPB_DataOfsEx, (DWORD)(b_data - b_vol)); /* Data offset [sector] */
+                       st_dword(buf + BPB_NumClusEx, n_clst);                                  /* Number of clusters */
+                       st_dword(buf + BPB_RootClusEx, 2 + tbl[0] + tbl[1]);    /* Root dir cluster # */
+                       st_dword(buf + BPB_VolIDEx, GET_FATTIME());                             /* VSN */
+                       st_word(buf + BPB_FSVerEx, 0x100);                                              /* Filesystem version (1.00) */
+                       for (buf[BPB_BytsPerSecEx] = 0, i = ss; i >>= 1; buf[BPB_BytsPerSecEx]++) ;     /* Log2 of sector size [byte] */
+                       for (buf[BPB_SecPerClusEx] = 0, i = sz_au; i >>= 1; buf[BPB_SecPerClusEx]++) ;  /* Log2 of cluster size [sector] */
+                       buf[BPB_NumFATsEx] = 1;                                 /* Number of FATs */
+                       buf[BPB_DrvNumEx] = 0x80;                               /* Drive number (for int13) */
+                       st_word(buf + BS_BootCodeEx, 0xFEEB);   /* Boot code (x86) */
+                       st_word(buf + BS_55AA, 0xAA55);                 /* Signature (placed here regardless of sector size) */
+                       for (i = sum = 0; i < ss; i++) {                /* VBR checksum */
+                               if (i != BPB_VolFlagEx && i != BPB_VolFlagEx + 1 && i != BPB_PercInUseEx) sum = xsum32(buf[i], sum);
+                       }
+                       if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+                       /* Extended bootstrap record (+1..+8) */
+                       mem_set(buf, 0, ss);
+                       st_word(buf + ss - 2, 0xAA55);  /* Signature (placed at end of sector) */
+                       for (j = 1; j < 9; j++) {
+                               for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ;      /* VBR checksum */
+                               if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+                       }
+                       /* OEM/Reserved record (+9..+10) */
+                       mem_set(buf, 0, ss);
+                       for ( ; j < 11; j++) {
+                               for (i = 0; i < ss; sum = xsum32(buf[i++], sum)) ;      /* VBR checksum */
+                               if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+                       }
+                       /* Sum record (+11) */
+                       for (i = 0; i < ss; i += 4) st_dword(buf + i, sum);             /* Fill with checksum value */
+                       if (disk_write(pdrv, buf, sect++, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+               }
+
+       } else
+#endif /* FF_FS_EXFAT */
+       {       /* Create an FAT/FAT32 volume */
+               do {
+                       pau = sz_au;
+                       /* Pre-determine number of clusters and FAT sub-type */
+                       if (fsty == FS_FAT32) { /* FAT32 volume */
+                               if (pau == 0) { /* AU auto-selection */
+                                       n = (DWORD)sz_vol / 0x20000;    /* Volume size in unit of 128KS */
+                                       for (i = 0, pau = 1; cst32[i] && cst32[i] <= n; i++, pau <<= 1) ;       /* Get from table */
+                               }
+                               n_clst = (DWORD)sz_vol / pau;   /* Number of clusters */
+                               sz_fat = (n_clst * 4 + 8 + ss - 1) / ss;        /* FAT size [sector] */
+                               sz_rsv = 32;    /* Number of reserved sectors */
+                               sz_dir = 0;             /* No static directory */
+                               if (n_clst <= MAX_FAT16 || n_clst > MAX_FAT32) LEAVE_MKFS(FR_MKFS_ABORTED);
+                       } else {                                /* FAT volume */
+                               if (pau == 0) { /* au auto-selection */
+                                       n = (DWORD)sz_vol / 0x1000;     /* Volume size in unit of 4KS */
+                                       for (i = 0, pau = 1; cst[i] && cst[i] <= n; i++, pau <<= 1) ;   /* Get from table */
+                               }
+                               n_clst = (DWORD)sz_vol / pau;
+                               if (n_clst > MAX_FAT12) {
+                                       n = n_clst * 2 + 4;             /* FAT size [byte] */
+                               } else {
+                                       fsty = FS_FAT12;
+                                       n = (n_clst * 3 + 1) / 2 + 3;   /* FAT size [byte] */
+                               }
+                               sz_fat = (n + ss - 1) / ss;             /* FAT size [sector] */
+                               sz_rsv = 1;                                             /* Number of reserved sectors */
+                               sz_dir = (DWORD)n_root * SZDIRE / ss;   /* Root dir size [sector] */
+                       }
+                       b_fat = b_vol + sz_rsv;                                         /* FAT base */
+                       b_data = b_fat + sz_fat * n_fat + sz_dir;       /* Data base */
+
+                       /* Align data area to erase block boundary (for flash memory media) */
+                       n = (DWORD)(((b_data + sz_blk - 1) & ~(sz_blk - 1)) - b_data);  /* Sectors to next nearest from current data base */
+                       if (fsty == FS_FAT32) {         /* FAT32: Move FAT */
+                               sz_rsv += n; b_fat += n;
+                       } else {                                        /* FAT: Expand FAT */
+                               if (n % n_fat) {        /* Adjust fractional error if needed */
+                                       n--; sz_rsv++; b_fat++;
+                               }
+                               sz_fat += n / n_fat;
+                       }
+
+                       /* Determine number of clusters and final check of validity of the FAT sub-type */
+                       if (sz_vol < b_data + pau * 16 - b_vol) LEAVE_MKFS(FR_MKFS_ABORTED);    /* Too small volume? */
+                       n_clst = ((DWORD)sz_vol - sz_rsv - sz_fat * n_fat - sz_dir) / pau;
+                       if (fsty == FS_FAT32) {
+                               if (n_clst <= MAX_FAT16) {      /* Too few clusters for FAT32? */
+                                       if (sz_au == 0 && (sz_au = pau / 2) != 0) continue;     /* Adjust cluster size and retry */
+                                       LEAVE_MKFS(FR_MKFS_ABORTED);
+                               }
+                       }
+                       if (fsty == FS_FAT16) {
+                               if (n_clst > MAX_FAT16) {       /* Too many clusters for FAT16 */
+                                       if (sz_au == 0 && (pau * 2) <= 64) {
+                                               sz_au = pau * 2; continue;              /* Adjust cluster size and retry */
+                                       }
+                                       if ((fsopt & FM_FAT32)) {
+                                               fsty = FS_FAT32; continue;      /* Switch type to FAT32 and retry */
+                                       }
+                                       if (sz_au == 0 && (sz_au = pau * 2) <= 128) continue;   /* Adjust cluster size and retry */
+                                       LEAVE_MKFS(FR_MKFS_ABORTED);
+                               }
+                               if  (n_clst <= MAX_FAT12) {     /* Too few clusters for FAT16 */
+                                       if (sz_au == 0 && (sz_au = pau * 2) <= 128) continue;   /* Adjust cluster size and retry */
+                                       LEAVE_MKFS(FR_MKFS_ABORTED);
+                               }
+                       }
+                       if (fsty == FS_FAT12 && n_clst > MAX_FAT12) LEAVE_MKFS(FR_MKFS_ABORTED);        /* Too many clusters for FAT12 */
+
+                       /* Ok, it is the valid cluster configuration */
+                       break;
+               } while (1);
+
+#if FF_USE_TRIM
+               lba[0] = b_vol; lba[1] = b_vol + sz_vol - 1;    /* Inform storage device that the volume area may be erased */
+               disk_ioctl(pdrv, CTRL_TRIM, lba);
+#endif
+               /* Create FAT VBR */
+               mem_set(buf, 0, ss);
+               mem_cpy(buf + BS_JmpBoot, "\xEB\xFE\x90" "MSDOS5.0", 11);/* Boot jump code (x86), OEM name */
+               st_word(buf + BPB_BytsPerSec, ss);                              /* Sector size [byte] */
+               buf[BPB_SecPerClus] = (BYTE)pau;                                /* Cluster size [sector] */
+               st_word(buf + BPB_RsvdSecCnt, (WORD)sz_rsv);    /* Size of reserved area */
+               buf[BPB_NumFATs] = (BYTE)n_fat;                                 /* Number of FATs */
+               st_word(buf + BPB_RootEntCnt, (WORD)((fsty == FS_FAT32) ? 0 : n_root)); /* Number of root directory entries */
+               if (sz_vol < 0x10000) {
+                       st_word(buf + BPB_TotSec16, (WORD)sz_vol);      /* Volume size in 16-bit LBA */
+               } else {
+                       st_dword(buf + BPB_TotSec32, (DWORD)sz_vol);    /* Volume size in 32-bit LBA */
+               }
+               buf[BPB_Media] = 0xF8;                                                  /* Media descriptor byte */
+               st_word(buf + BPB_SecPerTrk, 63);                               /* Number of sectors per track (for int13) */
+               st_word(buf + BPB_NumHeads, 255);                               /* Number of heads (for int13) */
+               st_dword(buf + BPB_HiddSec, (DWORD)b_vol);              /* Volume offset in the physical drive [sector] */
+               if (fsty == FS_FAT32) {
+                       st_dword(buf + BS_VolID32, GET_FATTIME());      /* VSN */
+                       st_dword(buf + BPB_FATSz32, sz_fat);            /* FAT size [sector] */
+                       st_dword(buf + BPB_RootClus32, 2);                      /* Root directory cluster # (2) */
+                       st_word(buf + BPB_FSInfo32, 1);                         /* Offset of FSINFO sector (VBR + 1) */
+                       st_word(buf + BPB_BkBootSec32, 6);                      /* Offset of backup VBR (VBR + 6) */
+                       buf[BS_DrvNum32] = 0x80;                                        /* Drive number (for int13) */
+                       buf[BS_BootSig32] = 0x29;                                       /* Extended boot signature */
+                       mem_cpy(buf + BS_VolLab32, "NO NAME    " "FAT32   ", 19);       /* Volume label, FAT signature */
+               } else {
+                       st_dword(buf + BS_VolID, GET_FATTIME());        /* VSN */
+                       st_word(buf + BPB_FATSz16, (WORD)sz_fat);       /* FAT size [sector] */
+                       buf[BS_DrvNum] = 0x80;                                          /* Drive number (for int13) */
+                       buf[BS_BootSig] = 0x29;                                         /* Extended boot signature */
+                       mem_cpy(buf + BS_VolLab, "NO NAME    " "FAT     ", 19); /* Volume label, FAT signature */
+               }
+               st_word(buf + BS_55AA, 0xAA55);                                 /* Signature (offset is fixed here regardless of sector size) */
+               if (disk_write(pdrv, buf, b_vol, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR); /* Write it to the VBR sector */
+
+               /* Create FSINFO record if needed */
+               if (fsty == FS_FAT32) {
+                       disk_write(pdrv, buf, b_vol + 6, 1);            /* Write backup VBR (VBR + 6) */
+                       mem_set(buf, 0, ss);
+                       st_dword(buf + FSI_LeadSig, 0x41615252);
+                       st_dword(buf + FSI_StrucSig, 0x61417272);
+                       st_dword(buf + FSI_Free_Count, n_clst - 1);     /* Number of free clusters */
+                       st_dword(buf + FSI_Nxt_Free, 2);                        /* Last allocated cluster# */
+                       st_word(buf + BS_55AA, 0xAA55);
+                       disk_write(pdrv, buf, b_vol + 7, 1);            /* Write backup FSINFO (VBR + 7) */
+                       disk_write(pdrv, buf, b_vol + 1, 1);            /* Write original FSINFO (VBR + 1) */
+               }
+
+               /* Initialize FAT area */
+               mem_set(buf, 0, sz_buf * ss);
+               sect = b_fat;           /* FAT start sector */
+               for (i = 0; i < n_fat; i++) {                   /* Initialize FATs each */
+                       if (fsty == FS_FAT32) {
+                               st_dword(buf + 0, 0xFFFFFFF8);  /* FAT[0] */
+                               st_dword(buf + 4, 0xFFFFFFFF);  /* FAT[1] */
+                               st_dword(buf + 8, 0x0FFFFFFF);  /* FAT[2] (root directory) */
+                       } else {
+                               st_dword(buf + 0, (fsty == FS_FAT12) ? 0xFFFFF8 : 0xFFFFFFF8);  /* FAT[0] and FAT[1] */
+                       }
+                       nsect = sz_fat;         /* Number of FAT sectors */
+                       do {    /* Fill FAT sectors */
+                               n = (nsect > sz_buf) ? sz_buf : nsect;
+                               if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+                               mem_set(buf, 0, ss);    /* Rest of FAT all are cleared */
+                               sect += n; nsect -= n;
+                       } while (nsect);
+               }
+
+               /* Initialize root directory (fill with zero) */
+               nsect = (fsty == FS_FAT32) ? pau : sz_dir;      /* Number of root directory sectors */
+               do {
+                       n = (nsect > sz_buf) ? sz_buf : nsect;
+                       if (disk_write(pdrv, buf, sect, (UINT)n) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+                       sect += n; nsect -= n;
+               } while (nsect);
+       }
+
+       /* A FAT volume has been created here */
+
+       /* Determine system ID in the MBR partition table */
+       if (FF_FS_EXFAT && fsty == FS_EXFAT) {
+               sys = 0x07;                     /* exFAT */
+       } else {
+               if (fsty == FS_FAT32) {
+                       sys = 0x0C;             /* FAT32X */
+               } else {
+                       if (sz_vol >= 0x10000) {
+                               sys = 0x06;     /* FAT12/16 (large) */
+                       } else {
+                               sys = (fsty == FS_FAT16) ? 0x04 : 0x01; /* FAT16 : FAT12 */
+                       }
+               }
+       }
+
+       /* Update partition information */
+       if (FF_MULTI_PARTITION && ipart != 0) { /* Volume is in the existing partition */
+               if (!FF_LBA64 || !(fsopt & 0x80)) {
+                       /* Update system ID in the partition table */
+                       if (disk_read(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);      /* Read the MBR */
+                       buf[MBR_Table + (ipart - 1) * SZ_PTE + PTE_System] = sys;                       /* Set system ID */
+                       if (disk_write(pdrv, buf, 0, 1) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);     /* Write it back to the MBR */
+               }
+       } else {                                                                /* Volume as a new single partition */
+               if (!(fsopt & FM_SFD)) {        /* Create partition table if not in SFD */
+                       lba[0] = sz_vol, lba[1] = 0;
+                       fr = create_partition(pdrv, lba, sys, buf);
+                       if (fr != FR_OK) LEAVE_MKFS(fr);
+               }
+       }
+
+       if (disk_ioctl(pdrv, CTRL_SYNC, 0) != RES_OK) LEAVE_MKFS(FR_DISK_ERR);
+
+       LEAVE_MKFS(FR_OK);
+}
+
+
+
+
+#if FF_MULTI_PARTITION
+/*-----------------------------------------------------------------------*/
+/* Create Partition Table on the Physical Drive                          */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_fdisk (
+       BYTE pdrv,                      /* Physical drive number */
+       const LBA_t ptbl[],     /* Pointer to the size table for each partitions */
+       void* work                      /* Pointer to the working buffer (null: use heap memory) */
+)
+{
+       BYTE *buf = (BYTE*)work;
+       DSTATUS stat;
+
+
+       stat = disk_initialize(pdrv);
+       if (stat & STA_NOINIT) return FR_NOT_READY;
+       if (stat & STA_PROTECT) return FR_WRITE_PROTECTED;
+#if FF_USE_LFN == 3
+       if (!buf) buf = ff_memalloc(FF_MAX_SS); /* Use heap memory for working buffer */
+#endif
+       if (!buf) return FR_NOT_ENOUGH_CORE;
+
+       LEAVE_MKFS(create_partition(pdrv, ptbl, 0x07, buf));
+}
+
+#endif /* FF_MULTI_PARTITION */
+#endif /* !FF_FS_READONLY && FF_USE_MKFS */
+
+
+
+
+#if FF_USE_STRFUNC
+#if FF_USE_LFN && FF_LFN_UNICODE && (FF_STRF_ENCODE < 0 || FF_STRF_ENCODE > 3)
+#error Wrong FF_STRF_ENCODE setting
+#endif
+/*-----------------------------------------------------------------------*/
+/* Get a String from the File                                            */
+/*-----------------------------------------------------------------------*/
+
+TCHAR* f_gets (
+       TCHAR* buff,    /* Pointer to the buffer to store read string */
+       int len,                /* Size of string buffer (items) */
+       FIL* fp                 /* Pointer to the file object */
+)
+{
+       int nc = 0;
+       TCHAR *p = buff;
+       BYTE s[4];
+       UINT rc;
+       DWORD dc;
+#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE <= 2
+       WCHAR wc;
+#endif
+#if FF_USE_LFN && FF_LFN_UNICODE && FF_STRF_ENCODE == 3
+       UINT ct;
+#endif
+
+#if FF_USE_LFN && FF_LFN_UNICODE                       /* With code conversion (Unicode API) */
+       /* Make a room for the character and terminator  */
+       if (FF_LFN_UNICODE == 1) len -= (FF_STRF_ENCODE == 0) ? 1 : 2;
+       if (FF_LFN_UNICODE == 2) len -= (FF_STRF_ENCODE == 0) ? 3 : 4;
+       if (FF_LFN_UNICODE == 3) len -= 1;
+       while (nc < len) {
+#if FF_STRF_ENCODE == 0                                /* Read a character in ANSI/OEM */
+               f_read(fp, s, 1, &rc);          /* Get a code unit */
+               if (rc != 1) break;                     /* EOF? */
+               wc = s[0];
+               if (dbc_1st((BYTE)wc)) {        /* DBC 1st byte? */
+                       f_read(fp, s, 1, &rc);  /* Get DBC 2nd byte */
+                       if (rc != 1 || !dbc_2nd(s[0])) continue;        /* Wrong code? */
+                       wc = wc << 8 | s[0];
+               }
+               dc = ff_oem2uni(wc, CODEPAGE);  /* OEM --> */
+               if (dc == 0) continue;
+#elif FF_STRF_ENCODE == 1 || FF_STRF_ENCODE == 2       /* Read a character in UTF-16LE/BE */
+               f_read(fp, s, 2, &rc);          /* Get a code unit */
+               if (rc != 2) break;                     /* EOF? */
+               dc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | s[1];
+               if (IsSurrogateL(dc)) continue; /* Broken surrogate pair? */
+               if (IsSurrogateH(dc)) {         /* High surrogate? */
+                       f_read(fp, s, 2, &rc);  /* Get low surrogate */
+                       if (rc != 2) break;             /* EOF? */
+                       wc = (FF_STRF_ENCODE == 1) ? ld_word(s) : s[0] << 8 | s[1];
+                       if (!IsSurrogateL(wc)) continue;        /* Broken surrogate pair? */
+                       dc = ((dc & 0x3FF) + 0x40) << 10 | (wc & 0x3FF);        /* Merge surrogate pair */
+               }
+#else  /* Read a character in UTF-8 */
+               f_read(fp, s, 1, &rc);          /* Get a code unit */
+               if (rc != 1) break;                     /* EOF? */
+               dc = s[0];
+               if (dc >= 0x80) {                       /* Multi-byte sequence? */
+                       ct = 0;
+                       if ((dc & 0xE0) == 0xC0) { dc &= 0x1F; ct = 1; }        /* 2-byte sequence? */
+                       if ((dc & 0xF0) == 0xE0) { dc &= 0x0F; ct = 2; }        /* 3-byte sequence? */
+                       if ((dc & 0xF8) == 0xF0) { dc &= 0x07; ct = 3; }        /* 4-byte sequence? */
+                       if (ct == 0) continue;
+                       f_read(fp, s, ct, &rc);         /* Get trailing bytes */
+                       if (rc != ct) break;
+                       rc = 0;
+                       do {    /* Merge the byte sequence */
+                               if ((s[rc] & 0xC0) != 0x80) break;
+                               dc = dc << 6 | (s[rc] & 0x3F);
+                       } while (++rc < ct);
+                       if (rc != ct || dc < 0x80 || IsSurrogate(dc) || dc >= 0x110000) continue;       /* Wrong encoding? */
+               }
+#endif
+               /* A code point is avaialble in dc to be output */
+
+               if (FF_USE_STRFUNC == 2 && dc == '\r') continue;        /* Strip \r off if needed */
+#if FF_LFN_UNICODE == 1        || FF_LFN_UNICODE == 3  /* Output it in UTF-16/32 encoding */
+               if (FF_LFN_UNICODE == 1 && dc >= 0x10000) {     /* Out of BMP at UTF-16? */
+                       *p++ = (TCHAR)(0xD800 | ((dc >> 10) - 0x40)); nc++;     /* Make and output high surrogate */
+                       dc = 0xDC00 | (dc & 0x3FF);             /* Make low surrogate */
+               }
+               *p++ = (TCHAR)dc; nc++;
+               if (dc == '\n') break;  /* End of line? */
+#elif FF_LFN_UNICODE == 2              /* Output it in UTF-8 encoding */
+               if (dc < 0x80) {        /* Single byte? */
+                       *p++ = (TCHAR)dc;
+                       nc++;
+                       if (dc == '\n') break;  /* End of line? */
+               } else {
+                       if (dc < 0x800) {               /* 2-byte sequence? */
+                               *p++ = (TCHAR)(0xC0 | (dc >> 6 & 0x1F));
+                               *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
+                               nc += 2;
+                       } else {
+                               if (dc < 0x10000) {     /* 3-byte sequence? */
+                                       *p++ = (TCHAR)(0xE0 | (dc >> 12 & 0x0F));
+                                       *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F));
+                                       *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
+                                       nc += 3;
+                               } else {                        /* 4-byte sequence? */
+                                       *p++ = (TCHAR)(0xF0 | (dc >> 18 & 0x07));
+                                       *p++ = (TCHAR)(0x80 | (dc >> 12 & 0x3F));
+                                       *p++ = (TCHAR)(0x80 | (dc >> 6 & 0x3F));
+                                       *p++ = (TCHAR)(0x80 | (dc >> 0 & 0x3F));
+                                       nc += 4;
+                               }
+                       }
+               }
+#endif
+       }
+
+#else                  /* Byte-by-byte read without any conversion (ANSI/OEM API) */
+       len -= 1;       /* Make a room for the terminator */
+       while (nc < len) {
+               f_read(fp, s, 1, &rc);  /* Get a byte */
+               if (rc != 1) break;             /* EOF? */
+               dc = s[0];
+               if (FF_USE_STRFUNC == 2 && dc == '\r') continue;
+               *p++ = (TCHAR)dc; nc++;
+               if (dc == '\n') break;
+       }
+#endif
+
+       *p = 0;         /* Terminate the string */
+       return nc ? buff : 0;   /* When no data read due to EOF or error, return with error. */
+}
+
+
+
+
+#if !FF_FS_READONLY
+#include <stdarg.h>
+/*-----------------------------------------------------------------------*/
+/* Put a Character to the File (sub-functions)                           */
+/*-----------------------------------------------------------------------*/
+
+/* Putchar output buffer and work area */
+
+typedef struct {
+       FIL *fp;                /* Ptr to the writing file */
+       int idx, nchr;  /* Write index of buf[] (-1:error), number of encoding units written */
+#if FF_USE_LFN && FF_LFN_UNICODE == 1
+       WCHAR hs;
+#elif FF_USE_LFN && FF_LFN_UNICODE == 2
+       BYTE bs[4];
+       UINT wi, ct;
+#endif
+       BYTE buf[64];   /* Write buffer */
+} putbuff;
+
+
+/* Buffered write with code conversion */
+
+static void putc_bfd (putbuff* pb, TCHAR c)
+{
+       UINT n;
+       int i, nc;
+#if FF_USE_LFN && FF_LFN_UNICODE
+       WCHAR hs, wc;
+#if FF_LFN_UNICODE == 2
+       DWORD dc;
+       TCHAR *tp;
+#endif
+#endif
+
+       if (FF_USE_STRFUNC == 2 && c == '\n') {  /* LF -> CRLF conversion */
+               putc_bfd(pb, '\r');
+       }
+
+       i = pb->idx;                    /* Write index of pb->buf[] */
+       if (i < 0) return;
+       nc = pb->nchr;                  /* Write unit counter */
+
+#if FF_USE_LFN && FF_LFN_UNICODE
+#if FF_LFN_UNICODE == 1                /* UTF-16 input */
+       if (IsSurrogateH(c)) {  /* High surrogate? */
+               pb->hs = c; return;     /* Save it for next */
+       }
+       hs = pb->hs; pb->hs = 0;
+       if (hs != 0) {                  /* There is a leading high surrogate */
+               if (!IsSurrogateL(c)) hs = 0;   /* Discard high surrogate if not a surrogate pair */
+       } else {
+               if (IsSurrogateL(c)) return;    /* Discard stray low surrogate */
+       }
+       wc = c;
+#elif FF_LFN_UNICODE == 2      /* UTF-8 input */
+       for (;;) {
+               if (pb->ct == 0) {      /* Out of multi-byte sequence? */
+                       pb->bs[pb->wi = 0] = (BYTE)c;   /* Save 1st byte */
+                       if ((BYTE)c < 0x80) break;                                      /* Single byte? */
+                       if (((BYTE)c & 0xE0) == 0xC0) pb->ct = 1;       /* 2-byte sequence? */
+                       if (((BYTE)c & 0xF0) == 0xE0) pb->ct = 2;       /* 3-byte sequence? */
+                       if (((BYTE)c & 0xF1) == 0xF0) pb->ct = 3;       /* 4-byte sequence? */
+                       return;
+               } else {                                /* In the multi-byte sequence */
+                       if (((BYTE)c & 0xC0) != 0x80) { /* Broken sequence? */
+                               pb->ct = 0; continue;
+                       }
+                       pb->bs[++pb->wi] = (BYTE)c;     /* Save the trailing byte */
+                       if (--pb->ct == 0) break;       /* End of multi-byte sequence? */
+                       return;
+               }
+       }
+       tp = (TCHAR*)pb->bs;
+       dc = tchar2uni(&tp);    /* UTF-8 ==> UTF-16 */
+       if (dc == 0xFFFFFFFF) return;   /* Wrong code? */
+       wc = (WCHAR)dc;
+       hs = (WCHAR)(dc >> 16);
+#elif FF_LFN_UNICODE == 3      /* UTF-32 input */
+       if (IsSurrogate(c) || c >= 0x110000) return;    /* Discard invalid code */
+       if (c >= 0x10000) {             /* Out of BMP? */
+               hs = (WCHAR)(0xD800 | ((c >> 10) - 0x40));      /* Make high surrogate */
+               wc = 0xDC00 | (c & 0x3FF);                                      /* Make low surrogate */
+       } else {
+               hs = 0;
+               wc = (WCHAR)c;
+       }
+#endif
+       /* A code point in UTF-16 is available in hs and wc */
+
+#if FF_STRF_ENCODE == 1                /* Write a code point in UTF-16LE */
+       if (hs != 0) {  /* Surrogate pair? */
+               st_word(&pb->buf[i], hs);
+               i += 2;
+               nc++;
+       }
+       st_word(&pb->buf[i], wc);
+       i += 2;
+#elif FF_STRF_ENCODE == 2      /* Write a code point in UTF-16BE */
+       if (hs != 0) {  /* Surrogate pair? */
+               pb->buf[i++] = (BYTE)(hs >> 8);
+               pb->buf[i++] = (BYTE)hs;
+               nc++;
+       }
+       pb->buf[i++] = (BYTE)(wc >> 8);
+       pb->buf[i++] = (BYTE)wc;
+#elif FF_STRF_ENCODE == 3      /* Write a code point in UTF-8 */
+       if (hs != 0) {  /* 4-byte sequence? */
+               nc += 3;
+               hs = (hs & 0x3FF) + 0x40;
+               pb->buf[i++] = (BYTE)(0xF0 | hs >> 8);
+               pb->buf[i++] = (BYTE)(0x80 | (hs >> 2 & 0x3F));
+               pb->buf[i++] = (BYTE)(0x80 | (hs & 3) << 4 | (wc >> 6 & 0x0F));
+               pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F));
+       } else {
+               if (wc < 0x80) {        /* Single byte? */
+                       pb->buf[i++] = (BYTE)wc;
+               } else {
+                       if (wc < 0x800) {       /* 2-byte sequence? */
+                               nc += 1;
+                               pb->buf[i++] = (BYTE)(0xC0 | wc >> 6);
+                       } else {                        /* 3-byte sequence */
+                               nc += 2;
+                               pb->buf[i++] = (BYTE)(0xE0 | wc >> 12);
+                               pb->buf[i++] = (BYTE)(0x80 | (wc >> 6 & 0x3F));
+                       }
+                       pb->buf[i++] = (BYTE)(0x80 | (wc & 0x3F));
+               }
+       }
+#else                                          /* Write a code point in ANSI/OEM */
+       if (hs != 0) return;
+       wc = ff_uni2oem(wc, CODEPAGE);  /* UTF-16 ==> ANSI/OEM */
+       if (wc == 0) return;
+       if (wc >= 0x100) {
+               pb->buf[i++] = (BYTE)(wc >> 8); nc++;
+       }
+       pb->buf[i++] = (BYTE)wc;
+#endif
+
+#else                                                                  /* ANSI/OEM input (without re-encoding) */
+       pb->buf[i++] = (BYTE)c;
+#endif
+
+       if (i >= (int)(sizeof pb->buf) - 4) {   /* Write buffered characters to the file */
+               f_write(pb->fp, pb->buf, (UINT)i, &n);
+               i = (n == (UINT)i) ? 0 : -1;
+       }
+       pb->idx = i;
+       pb->nchr = nc + 1;
+}
+
+
+/* Flush remaining characters in the buffer */
+
+static int putc_flush (putbuff* pb)
+{
+       UINT nw;
+
+       if (   pb->idx >= 0     /* Flush buffered characters to the file */
+               && f_write(pb->fp, pb->buf, (UINT)pb->idx, &nw) == FR_OK
+               && (UINT)pb->idx == nw) return pb->nchr;
+       return EOF;
+}
+
+
+/* Initialize write buffer */
+
+static void putc_init (putbuff* pb, FIL* fp)
+{
+       mem_set(pb, 0, sizeof (putbuff));
+       pb->fp = fp;
+}
+
+
+
+int f_putc (
+       TCHAR c,        /* A character to be output */
+       FIL* fp         /* Pointer to the file object */
+)
+{
+       putbuff pb;
+
+
+       putc_init(&pb, fp);
+       putc_bfd(&pb, c);       /* Put the character */
+       return putc_flush(&pb);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a String to the File                                              */
+/*-----------------------------------------------------------------------*/
+
+int f_puts (
+       const TCHAR* str,       /* Pointer to the string to be output */
+       FIL* fp                         /* Pointer to the file object */
+)
+{
+       putbuff pb;
+
+
+       putc_init(&pb, fp);
+       while (*str) putc_bfd(&pb, *str++);             /* Put the string */
+       return putc_flush(&pb);
+}
+
+
+
+
+/*-----------------------------------------------------------------------*/
+/* Put a Formatted String to the File                                    */
+/*-----------------------------------------------------------------------*/
+
+int f_printf (
+       FIL* fp,                        /* Pointer to the file object */
+       const TCHAR* fmt,       /* Pointer to the format string */
+       ...                                     /* Optional arguments... */
+)
+{
+       va_list arp;
+       putbuff pb;
+       BYTE f, r;
+       UINT i, j, w;
+       DWORD v;
+       TCHAR c, d, str[32], *p;
+
+
+       putc_init(&pb, fp);
+
+       va_start(arp, fmt);
+
+       for (;;) {
+               c = *fmt++;
+               if (c == 0) break;                      /* End of string */
+               if (c != '%') {                         /* Non escape character */
+                       putc_bfd(&pb, c);
+                       continue;
+               }
+               w = f = 0;
+               c = *fmt++;
+               if (c == '0') {                         /* Flag: '0' padding */
+                       f = 1; c = *fmt++;
+               } else {
+                       if (c == '-') {                 /* Flag: left justified */
+                               f = 2; c = *fmt++;
+                       }
+               }
+               if (c == '*') {                         /* Minimum width by argument */
+                       w = va_arg(arp, int);
+                       c = *fmt++;
+               } else {
+                       while (IsDigit(c)) {    /* Minimum width */
+                               w = w * 10 + c - '0';
+                               c = *fmt++;
+                       }
+               }
+               if (c == 'l' || c == 'L') {     /* Type prefix: Size is long int */
+                       f |= 4; c = *fmt++;
+               }
+               if (c == 0) break;
+               d = c;
+               if (IsLower(d)) d -= 0x20;
+               switch (d) {                            /* Atgument type is... */
+               case 'S' :                                      /* String */
+                       p = va_arg(arp, TCHAR*);
+                       for (j = 0; p[j]; j++) ;
+                       if (!(f & 2)) {                                         /* Right padded */
+                               while (j++ < w) putc_bfd(&pb, ' ') ;
+                       }
+                       while (*p) putc_bfd(&pb, *p++) ;                /* String body */
+                       while (j++ < w) putc_bfd(&pb, ' ') ;    /* Left padded */
+                       continue;
+
+               case 'C' :                                      /* Character */
+                       putc_bfd(&pb, (TCHAR)va_arg(arp, int)); continue;
+
+               case 'B' :                                      /* Unsigned binary */
+                       r = 2; break;
+
+               case 'O' :                                      /* Unsigned octal */
+                       r = 8; break;
+
+               case 'D' :                                      /* Signed decimal */
+               case 'U' :                                      /* Unsigned decimal */
+                       r = 10; break;
+
+               case 'X' :                                      /* Unsigned hexdecimal */
+                       r = 16; break;
+
+               default:                                        /* Unknown type (pass-through) */
+                       putc_bfd(&pb, c); continue;
+               }
+
+               /* Get an argument and put it in numeral */
+               v = (f & 4) ? (DWORD)va_arg(arp, long) : ((d == 'D') ? (DWORD)(long)va_arg(arp, int) : (DWORD)va_arg(arp, unsigned int));
+               if (d == 'D' && (v & 0x80000000)) {
+                       v = 0 - v;
+                       f |= 8;
+               }
+               i = 0;
+               do {
+                       d = (TCHAR)(v % r); v /= r;
+                       if (d > 9) d += (c == 'x') ? 0x27 : 0x07;
+                       str[i++] = d + '0';
+               } while (v && i < sizeof str / sizeof *str);
+               if (f & 8) str[i++] = '-';
+               j = i; d = (f & 1) ? '0' : ' ';
+               if (!(f & 2)) {
+                       while (j++ < w) putc_bfd(&pb, d);       /* Right pad */
+               }
+               do {
+                       putc_bfd(&pb, str[--i]);                        /* Number body */
+               } while (i);
+               while (j++ < w) putc_bfd(&pb, d);               /* Left pad */
+       }
+
+       va_end(arp);
+
+       return putc_flush(&pb);
+}
+
+#endif /* !FF_FS_READONLY */
+#endif /* FF_USE_STRFUNC */
+
+
+
+#if FF_CODE_PAGE == 0
+/*-----------------------------------------------------------------------*/
+/* Set Active Codepage for the Path Name                                 */
+/*-----------------------------------------------------------------------*/
+
+FRESULT f_setcp (
+       WORD cp         /* Value to be set as active code page */
+)
+{
+       static const WORD       validcp[] = {  437,   720,   737,   771,   775,   850,   852,   857,   860,   861,   862,   863,   864,   865,   866,   869,   932,   936,   949,   950, 0};
+       static const BYTE* const tables[] = {Ct437, Ct720, Ct737, Ct771, Ct775, Ct850, Ct852, Ct857, Ct860, Ct861, Ct862, Ct863, Ct864, Ct865, Ct866, Ct869, Dc932, Dc936, Dc949, Dc950, 0};
+       UINT i;
+
+
+       for (i = 0; validcp[i] != 0 && validcp[i] != cp; i++) ; /* Find the code page */
+       if (validcp[i] != cp) return FR_INVALID_PARAMETER;      /* Not found? */
+
+       CodePage = cp;
+       if (cp >= 900) {        /* DBCS */
+               ExCvt = 0;
+               DbcTbl = tables[i];
+       } else {                        /* SBCS */
+               ExCvt = tables[i];
+               DbcTbl = 0;
+       }
+       return FR_OK;
+}
+#endif /* FF_CODE_PAGE == 0 */
+
diff --git a/Ventoy2Disk/Ventoy2Disk/ff14/source/ff.h b/Ventoy2Disk/Ventoy2Disk/ff14/source/ff.h
new file mode 100644 (file)
index 0000000..5225041
--- /dev/null
@@ -0,0 +1,426 @@
+/*----------------------------------------------------------------------------/
+/  FatFs - Generic FAT Filesystem module  R0.14                               /
+/-----------------------------------------------------------------------------/
+/
+/ Copyright (C) 2019, ChaN, all right reserved.
+/
+/ FatFs module is an open source software. Redistribution and use of FatFs in
+/ source and binary forms, with or without modification, are permitted provided
+/ that the following condition is met:
+
+/ 1. Redistributions of source code must retain the above copyright notice,
+/    this condition and the following disclaimer.
+/
+/ This software is provided by the copyright holder and contributors "AS IS"
+/ and any warranties related to this software are DISCLAIMED.
+/ The copyright owner or contributors be NOT LIABLE for any damages caused
+/ by use of this software.
+/
+/----------------------------------------------------------------------------*/
+
+
+#ifndef FF_DEFINED
+#define FF_DEFINED     86606   /* Revision ID */
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "ffconf.h"            /* FatFs configuration options */
+
+#if FF_DEFINED != FFCONF_DEF
+#error Wrong configuration file (ffconf.h).
+#endif
+
+
+/* Integer types used for FatFs API */
+
+#if defined(_WIN32)    /* Main development platform */
+#define FF_INTDEF 2
+#include <windows.h>
+typedef unsigned __int64 QWORD;
+#elif (defined(__STDC_VERSION__) && __STDC_VERSION__ >= 199901L) || defined(__cplusplus)       /* C99 or later */
+#define FF_INTDEF 2
+#include <stdint.h>
+typedef unsigned int   UINT;   /* int must be 16-bit or 32-bit */
+typedef unsigned char  BYTE;   /* char must be 8-bit */
+typedef uint16_t               WORD;   /* 16-bit unsigned integer */
+typedef uint32_t               DWORD;  /* 32-bit unsigned integer */
+typedef uint64_t               QWORD;  /* 64-bit unsigned integer */
+typedef WORD                   WCHAR;  /* UTF-16 character type */
+#else          /* Earlier than C99 */
+#define FF_INTDEF 1
+typedef unsigned int   UINT;   /* int must be 16-bit or 32-bit */
+typedef unsigned char  BYTE;   /* char must be 8-bit */
+typedef unsigned short WORD;   /* 16-bit unsigned integer */
+typedef unsigned long  DWORD;  /* 32-bit unsigned integer */
+typedef WORD                   WCHAR;  /* UTF-16 character type */
+#endif
+
+
+/* Definitions of volume management */
+
+#if FF_MULTI_PARTITION         /* Multiple partition configuration */
+typedef struct {
+       BYTE pd;        /* Physical drive number */
+       BYTE pt;        /* Partition: 0:Auto detect, 1-4:Forced partition) */
+} PARTITION;
+extern PARTITION VolToPart[];  /* Volume - Partition mapping table */
+#endif
+
+#if FF_STR_VOLUME_ID
+#ifndef FF_VOLUME_STRS
+extern const char* VolumeStr[FF_VOLUMES];      /* User defied volume ID */
+#endif
+#endif
+
+
+
+/* Type of path name strings on FatFs API */
+
+#ifndef _INC_TCHAR
+#define _INC_TCHAR
+
+#if FF_USE_LFN && FF_LFN_UNICODE == 1  /* Unicode in UTF-16 encoding */
+typedef WCHAR TCHAR;
+#define _T(x) L ## x
+#define _TEXT(x) L ## x
+#elif FF_USE_LFN && FF_LFN_UNICODE == 2        /* Unicode in UTF-8 encoding */
+typedef char TCHAR;
+#define _T(x) u8 ## x
+#define _TEXT(x) u8 ## x
+#elif FF_USE_LFN && FF_LFN_UNICODE == 3        /* Unicode in UTF-32 encoding */
+typedef DWORD TCHAR;
+#define _T(x) U ## x
+#define _TEXT(x) U ## x
+#elif FF_USE_LFN && (FF_LFN_UNICODE < 0 || FF_LFN_UNICODE > 3)
+#error Wrong FF_LFN_UNICODE setting
+#else                                                                  /* ANSI/OEM code in SBCS/DBCS */
+typedef char TCHAR;
+#define _T(x) x
+#define _TEXT(x) x
+#endif
+
+#endif
+
+
+
+/* Type of file size and LBA variables */
+
+#if FF_FS_EXFAT
+#if FF_INTDEF != 2
+#error exFAT feature wants C99 or later
+#endif
+typedef QWORD FSIZE_t;
+#if FF_LBA64
+typedef QWORD LBA_t;
+#else
+typedef DWORD LBA_t;
+#endif
+#else
+#if FF_LBA64
+#error exFAT needs to be enabled when enable 64-bit LBA
+#endif
+typedef DWORD FSIZE_t;
+typedef DWORD LBA_t;
+#endif
+
+
+
+/* Filesystem object structure (FATFS) */
+
+typedef struct {
+       BYTE    fs_type;                /* Filesystem type (0:not mounted) */
+       BYTE    pdrv;                   /* Associated physical drive */
+       BYTE    n_fats;                 /* Number of FATs (1 or 2) */
+       BYTE    wflag;                  /* win[] flag (b0:dirty) */
+       BYTE    fsi_flag;               /* FSINFO flags (b7:disabled, b0:dirty) */
+       WORD    id;                             /* Volume mount ID */
+       WORD    n_rootdir;              /* Number of root directory entries (FAT12/16) */
+       WORD    csize;                  /* Cluster size [sectors] */
+#if FF_MAX_SS != FF_MIN_SS
+       WORD    ssize;                  /* Sector size (512, 1024, 2048 or 4096) */
+#endif
+#if FF_USE_LFN
+       WCHAR*  lfnbuf;                 /* LFN working buffer */
+#endif
+#if FF_FS_EXFAT
+       BYTE*   dirbuf;                 /* Directory entry block scratchpad buffer for exFAT */
+#endif
+#if FF_FS_REENTRANT
+       FF_SYNC_t       sobj;           /* Identifier of sync object */
+#endif
+#if !FF_FS_READONLY
+       DWORD   last_clst;              /* Last allocated cluster */
+       DWORD   free_clst;              /* Number of free clusters */
+#endif
+#if FF_FS_RPATH
+       DWORD   cdir;                   /* Current directory start cluster (0:root) */
+#if FF_FS_EXFAT
+       DWORD   cdc_scl;                /* Containing directory start cluster (invalid when cdir is 0) */
+       DWORD   cdc_size;               /* b31-b8:Size of containing directory, b7-b0: Chain status */
+       DWORD   cdc_ofs;                /* Offset in the containing directory (invalid when cdir is 0) */
+#endif
+#endif
+       DWORD   n_fatent;               /* Number of FAT entries (number of clusters + 2) */
+       DWORD   fsize;                  /* Size of an FAT [sectors] */
+       LBA_t   volbase;                /* Volume base sector */
+       LBA_t   fatbase;                /* FAT base sector */
+       LBA_t   dirbase;                /* Root directory base sector/cluster */
+       LBA_t   database;               /* Data base sector */
+#if FF_FS_EXFAT
+       LBA_t   bitbase;                /* Allocation bitmap base sector */
+#endif
+       LBA_t   winsect;                /* Current sector appearing in the win[] */
+       BYTE    win[FF_MAX_SS]; /* Disk access window for Directory, FAT (and file data at tiny cfg) */
+} FATFS;
+
+
+
+/* Object ID and allocation information (FFOBJID) */
+
+typedef struct {
+       FATFS*  fs;                             /* Pointer to the hosting volume of this object */
+       WORD    id;                             /* Hosting volume mount ID */
+       BYTE    attr;                   /* Object attribute */
+       BYTE    stat;                   /* Object chain status (b1-0: =0:not contiguous, =2:contiguous, =3:fragmented in this session, b2:sub-directory stretched) */
+       DWORD   sclust;                 /* Object data start cluster (0:no cluster or root directory) */
+       FSIZE_t objsize;                /* Object size (valid when sclust != 0) */
+#if FF_FS_EXFAT
+       DWORD   n_cont;                 /* Size of first fragment - 1 (valid when stat == 3) */
+       DWORD   n_frag;                 /* Size of last fragment needs to be written to FAT (valid when not zero) */
+       DWORD   c_scl;                  /* Containing directory start cluster (valid when sclust != 0) */
+       DWORD   c_size;                 /* b31-b8:Size of containing directory, b7-b0: Chain status (valid when c_scl != 0) */
+       DWORD   c_ofs;                  /* Offset in the containing directory (valid when file object and sclust != 0) */
+#endif
+#if FF_FS_LOCK
+       UINT    lockid;                 /* File lock ID origin from 1 (index of file semaphore table Files[]) */
+#endif
+} FFOBJID;
+
+
+
+/* File object structure (FIL) */
+
+typedef struct {
+       FFOBJID obj;                    /* Object identifier (must be the 1st member to detect invalid object pointer) */
+       BYTE    flag;                   /* File status flags */
+       BYTE    err;                    /* Abort flag (error code) */
+       FSIZE_t fptr;                   /* File read/write pointer (Zeroed on file open) */
+       DWORD   clust;                  /* Current cluster of fpter (invalid when fptr is 0) */
+       LBA_t   sect;                   /* Sector number appearing in buf[] (0:invalid) */
+#if !FF_FS_READONLY
+       LBA_t   dir_sect;               /* Sector number containing the directory entry (not used at exFAT) */
+       BYTE*   dir_ptr;                /* Pointer to the directory entry in the win[] (not used at exFAT) */
+#endif
+#if FF_USE_FASTSEEK
+       DWORD*  cltbl;                  /* Pointer to the cluster link map table (nulled on open, set by application) */
+#endif
+#if !FF_FS_TINY
+       BYTE    buf[FF_MAX_SS]; /* File private data read/write window */
+#endif
+} FIL;
+
+
+
+/* Directory object structure (DIR) */
+
+typedef struct {
+       FFOBJID obj;                    /* Object identifier */
+       DWORD   dptr;                   /* Current read/write offset */
+       DWORD   clust;                  /* Current cluster */
+       LBA_t   sect;                   /* Current sector (0:Read operation has terminated) */
+       BYTE*   dir;                    /* Pointer to the directory item in the win[] */
+       BYTE    fn[12];                 /* SFN (in/out) {body[8],ext[3],status[1]} */
+#if FF_USE_LFN
+       DWORD   blk_ofs;                /* Offset of current entry block being processed (0xFFFFFFFF:Invalid) */
+#endif
+#if FF_USE_FIND
+       const TCHAR* pat;               /* Pointer to the name matching pattern */
+#endif
+} DIR;
+
+
+
+/* File information structure (FILINFO) */
+
+typedef struct {
+       FSIZE_t fsize;                  /* File size */
+       WORD    fdate;                  /* Modified date */
+       WORD    ftime;                  /* Modified time */
+       BYTE    fattrib;                /* File attribute */
+#if FF_USE_LFN
+       TCHAR   altname[FF_SFN_BUF + 1];/* Altenative file name */
+       TCHAR   fname[FF_LFN_BUF + 1];  /* Primary file name */
+#else
+       TCHAR   fname[12 + 1];  /* File name */
+#endif
+} FILINFO;
+
+
+
+/* Format parameter structure (MKFS_PARM) */
+
+typedef struct {
+       BYTE fmt;                       /* Format option (FM_FAT, FM_FAT32, FM_EXFAT and FM_SFD) */
+       BYTE n_fat;                     /* Number of FATs */
+       UINT align;                     /* Data area alignment (sector) */
+       UINT n_root;            /* Number of root directory entries */
+       DWORD au_size;          /* Cluster size (byte) */
+} MKFS_PARM;
+
+
+
+/* File function return code (FRESULT) */
+
+typedef enum {
+       FR_OK = 0,                              /* (0) Succeeded */
+       FR_DISK_ERR,                    /* (1) A hard error occurred in the low level disk I/O layer */
+       FR_INT_ERR,                             /* (2) Assertion failed */
+       FR_NOT_READY,                   /* (3) The physical drive cannot work */
+       FR_NO_FILE,                             /* (4) Could not find the file */
+       FR_NO_PATH,                             /* (5) Could not find the path */
+       FR_INVALID_NAME,                /* (6) The path name format is invalid */
+       FR_DENIED,                              /* (7) Access denied due to prohibited access or directory full */
+       FR_EXIST,                               /* (8) Access denied due to prohibited access */
+       FR_INVALID_OBJECT,              /* (9) The file/directory object is invalid */
+       FR_WRITE_PROTECTED,             /* (10) The physical drive is write protected */
+       FR_INVALID_DRIVE,               /* (11) The logical drive number is invalid */
+       FR_NOT_ENABLED,                 /* (12) The volume has no work area */
+       FR_NO_FILESYSTEM,               /* (13) There is no valid FAT volume */
+       FR_MKFS_ABORTED,                /* (14) The f_mkfs() aborted due to any problem */
+       FR_TIMEOUT,                             /* (15) Could not get a grant to access the volume within defined period */
+       FR_LOCKED,                              /* (16) The operation is rejected according to the file sharing policy */
+       FR_NOT_ENOUGH_CORE,             /* (17) LFN working buffer could not be allocated */
+       FR_TOO_MANY_OPEN_FILES, /* (18) Number of open files > FF_FS_LOCK */
+       FR_INVALID_PARAMETER    /* (19) Given parameter is invalid */
+} FRESULT;
+
+
+
+/*--------------------------------------------------------------*/
+/* FatFs module application interface                           */
+
+FRESULT f_open (FIL* fp, const TCHAR* path, BYTE mode);                                /* Open or create a file */
+FRESULT f_close (FIL* fp);                                                                                     /* Close an open file object */
+FRESULT f_read (FIL* fp, void* buff, UINT btr, UINT* br);                      /* Read data from the file */
+FRESULT f_write (FIL* fp, const void* buff, UINT btw, UINT* bw);       /* Write data to the file */
+FRESULT f_lseek (FIL* fp, FSIZE_t ofs);                                                                /* Move file pointer of the file object */
+FRESULT f_truncate (FIL* fp);                                                                          /* Truncate the file */
+FRESULT f_sync (FIL* fp);                                                                                      /* Flush cached data of the writing file */
+FRESULT f_opendir (DIR* dp, const TCHAR* path);                                                /* Open a directory */
+FRESULT f_closedir (DIR* dp);                                                                          /* Close an open directory */
+FRESULT f_readdir (DIR* dp, FILINFO* fno);                                                     /* Read a directory item */
+FRESULT f_findfirst (DIR* dp, FILINFO* fno, const TCHAR* path, const TCHAR* pattern);  /* Find first file */
+FRESULT f_findnext (DIR* dp, FILINFO* fno);                                                    /* Find next file */
+FRESULT f_mkdir (const TCHAR* path);                                                           /* Create a sub directory */
+FRESULT f_unlink (const TCHAR* path);                                                          /* Delete an existing file or directory */
+FRESULT f_rename (const TCHAR* path_old, const TCHAR* path_new);       /* Rename/Move a file or directory */
+FRESULT f_stat (const TCHAR* path, FILINFO* fno);                                      /* Get file status */
+FRESULT f_chmod (const TCHAR* path, BYTE attr, BYTE mask);                     /* Change attribute of a file/dir */
+FRESULT f_utime (const TCHAR* path, const FILINFO* fno);                       /* Change timestamp of a file/dir */
+FRESULT f_chdir (const TCHAR* path);                                                           /* Change current directory */
+FRESULT f_chdrive (const TCHAR* path);                                                         /* Change current drive */
+FRESULT f_getcwd (TCHAR* buff, UINT len);                                                      /* Get current directory */
+FRESULT f_getfree (const TCHAR* path, DWORD* nclst, FATFS** fatfs);    /* Get number of free clusters on the drive */
+FRESULT f_getlabel (const TCHAR* path, TCHAR* label, DWORD* vsn);      /* Get volume label */
+FRESULT f_setlabel (const TCHAR* label);                                                       /* Set volume label */
+FRESULT f_forward (FIL* fp, UINT(*func)(const BYTE*,UINT), UINT btf, UINT* bf);        /* Forward data to the stream */
+FRESULT f_expand (FIL* fp, FSIZE_t fsz, BYTE opt);                                     /* Allocate a contiguous block to the file */
+FRESULT f_mount (FATFS* fs, const TCHAR* path, BYTE opt);                      /* Mount/Unmount a logical drive */
+FRESULT f_mkfs (const TCHAR* path, const MKFS_PARM* opt, void* work, UINT len);        /* Create a FAT volume */
+FRESULT f_fdisk (BYTE pdrv, const LBA_t ptbl[], void* work);           /* Divide a physical drive into some partitions */
+FRESULT f_setcp (WORD cp);                                                                                     /* Set current code page */
+int f_putc (TCHAR c, FIL* fp);                                                                         /* Put a character to the file */
+int f_puts (const TCHAR* str, FIL* cp);                                                                /* Put a string to the file */
+int f_printf (FIL* fp, const TCHAR* str, ...);                                         /* Put a formatted string to the file */
+TCHAR* f_gets (TCHAR* buff, int len, FIL* fp);                                         /* Get a string from the file */
+
+#define f_eof(fp) ((int)((fp)->fptr == (fp)->obj.objsize))
+#define f_error(fp) ((fp)->err)
+#define f_tell(fp) ((fp)->fptr)
+#define f_size(fp) ((fp)->obj.objsize)
+#define f_rewind(fp) f_lseek((fp), 0)
+#define f_rewinddir(dp) f_readdir((dp), 0)
+#define f_rmdir(path) f_unlink(path)
+#define f_unmount(path) f_mount(0, path, 0)
+
+#ifndef EOF
+#define EOF (-1)
+#endif
+
+
+
+
+/*--------------------------------------------------------------*/
+/* Additional user defined functions                            */
+
+/* RTC function */
+#if !FF_FS_READONLY && !FF_FS_NORTC
+DWORD get_fattime (void);
+#endif
+
+/* LFN support functions */
+#if FF_USE_LFN >= 1                                            /* Code conversion (defined in unicode.c) */
+WCHAR ff_oem2uni (WCHAR oem, WORD cp); /* OEM code to Unicode conversion */
+WCHAR ff_uni2oem (DWORD uni, WORD cp); /* Unicode to OEM code conversion */
+DWORD ff_wtoupper (DWORD uni);                 /* Unicode upper-case conversion */
+#endif
+#if FF_USE_LFN == 3                                            /* Dynamic memory allocation */
+void* ff_memalloc (UINT msize);                        /* Allocate memory block */
+void ff_memfree (void* mblock);                        /* Free memory block */
+#endif
+
+/* Sync functions */
+#if FF_FS_REENTRANT
+int ff_cre_syncobj (BYTE vol, FF_SYNC_t* sobj);        /* Create a sync object */
+int ff_req_grant (FF_SYNC_t sobj);             /* Lock sync object */
+void ff_rel_grant (FF_SYNC_t sobj);            /* Unlock sync object */
+int ff_del_syncobj (FF_SYNC_t sobj);   /* Delete a sync object */
+#endif
+
+
+
+
+/*--------------------------------------------------------------*/
+/* Flags and offset address                                     */
+
+
+/* File access mode and open method flags (3rd argument of f_open) */
+#define        FA_READ                         0x01
+#define        FA_WRITE                        0x02
+#define        FA_OPEN_EXISTING        0x00
+#define        FA_CREATE_NEW           0x04
+#define        FA_CREATE_ALWAYS        0x08
+#define        FA_OPEN_ALWAYS          0x10
+#define        FA_OPEN_APPEND          0x30
+
+/* Fast seek controls (2nd argument of f_lseek) */
+#define CREATE_LINKMAP ((FSIZE_t)0 - 1)
+
+/* Format options (2nd argument of f_mkfs) */
+#define FM_FAT         0x01
+#define FM_FAT32       0x02
+#define FM_EXFAT       0x04
+#define FM_ANY         0x07
+#define FM_SFD         0x08
+
+/* Filesystem type (FATFS.fs_type) */
+#define FS_FAT12       1
+#define FS_FAT16       2
+#define FS_FAT32       3
+#define FS_EXFAT       4
+
+/* File attribute bits for directory entry (FILINFO.fattrib) */
+#define        AM_RDO  0x01    /* Read only */
+#define        AM_HID  0x02    /* Hidden */
+#define        AM_SYS  0x04    /* System */
+#define AM_DIR 0x10    /* Directory */
+#define AM_ARC 0x20    /* Archive */
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* FF_DEFINED */
diff --git a/Ventoy2Disk/Ventoy2Disk/ff14/source/ffconf.h b/Ventoy2Disk/Ventoy2Disk/ff14/source/ffconf.h
new file mode 100644 (file)
index 0000000..229d865
--- /dev/null
@@ -0,0 +1,298 @@
+/*---------------------------------------------------------------------------/
+/  FatFs Functional Configurations
+/---------------------------------------------------------------------------*/
+
+#define FFCONF_DEF     86606   /* Revision ID */
+
+/*---------------------------------------------------------------------------/
+/ Function Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_FS_READONLY 0
+/* This option switches read-only configuration. (0:Read/Write or 1:Read-only)
+/  Read-only configuration removes writing API functions, f_write(), f_sync(),
+/  f_unlink(), f_mkdir(), f_chmod(), f_rename(), f_truncate(), f_getfree()
+/  and optional writing functions as well. */
+
+
+#define FF_FS_MINIMIZE 3
+/* This option defines minimization level to remove some basic API functions.
+/
+/   0: Basic functions are fully enabled.
+/   1: f_stat(), f_getfree(), f_unlink(), f_mkdir(), f_truncate() and f_rename()
+/      are removed.
+/   2: f_opendir(), f_readdir() and f_closedir() are removed in addition to 1.
+/   3: f_lseek() function is removed in addition to 2. */
+
+
+#define FF_USE_STRFUNC 0
+/* This option switches string functions, f_gets(), f_putc(), f_puts() and f_printf().
+/
+/  0: Disable string functions.
+/  1: Enable without LF-CRLF conversion.
+/  2: Enable with LF-CRLF conversion. */
+
+
+#define FF_USE_FIND            0
+/* This option switches filtered directory read functions, f_findfirst() and
+/  f_findnext(). (0:Disable, 1:Enable 2:Enable with matching altname[] too) */
+
+
+#define FF_USE_MKFS            1
+/* This option switches f_mkfs() function. (0:Disable or 1:Enable) */
+
+
+#define FF_USE_FASTSEEK        0
+/* This option switches fast seek function. (0:Disable or 1:Enable) */
+
+
+#define FF_USE_EXPAND  0
+/* This option switches f_expand function. (0:Disable or 1:Enable) */
+
+
+#define FF_USE_CHMOD   0
+/* This option switches attribute manipulation functions, f_chmod() and f_utime().
+/  (0:Disable or 1:Enable) Also FF_FS_READONLY needs to be 0 to enable this option. */
+
+
+#define FF_USE_LABEL   1
+/* This option switches volume label functions, f_getlabel() and f_setlabel().
+/  (0:Disable or 1:Enable) */
+
+
+#define FF_USE_FORWARD 0
+/* This option switches f_forward() function. (0:Disable or 1:Enable) */
+
+
+/*---------------------------------------------------------------------------/
+/ Locale and Namespace Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_CODE_PAGE   437
+/* This option specifies the OEM code page to be used on the target system.
+/  Incorrect code page setting can cause a file open failure.
+/
+/   437 - U.S.
+/   720 - Arabic
+/   737 - Greek
+/   771 - KBL
+/   775 - Baltic
+/   850 - Latin 1
+/   852 - Latin 2
+/   855 - Cyrillic
+/   857 - Turkish
+/   860 - Portuguese
+/   861 - Icelandic
+/   862 - Hebrew
+/   863 - Canadian French
+/   864 - Arabic
+/   865 - Nordic
+/   866 - Russian
+/   869 - Greek 2
+/   932 - Japanese (DBCS)
+/   936 - Simplified Chinese (DBCS)
+/   949 - Korean (DBCS)
+/   950 - Traditional Chinese (DBCS)
+/     0 - Include all code pages above and configured by f_setcp()
+*/
+
+
+#define FF_USE_LFN             3
+#define FF_MAX_LFN             255
+/* The FF_USE_LFN switches the support for LFN (long file name).
+/
+/   0: Disable LFN. FF_MAX_LFN has no effect.
+/   1: Enable LFN with static  working buffer on the BSS. Always NOT thread-safe.
+/   2: Enable LFN with dynamic working buffer on the STACK.
+/   3: Enable LFN with dynamic working buffer on the HEAP.
+/
+/  To enable the LFN, ffunicode.c needs to be added to the project. The LFN function
+/  requiers certain internal working buffer occupies (FF_MAX_LFN + 1) * 2 bytes and
+/  additional (FF_MAX_LFN + 44) / 15 * 32 bytes when exFAT is enabled.
+/  The FF_MAX_LFN defines size of the working buffer in UTF-16 code unit and it can
+/  be in range of 12 to 255. It is recommended to be set it 255 to fully support LFN
+/  specification.
+/  When use stack for the working buffer, take care on stack overflow. When use heap
+/  memory for the working buffer, memory management functions, ff_memalloc() and
+/  ff_memfree() exemplified in ffsystem.c, need to be added to the project. */
+
+
+#define FF_LFN_UNICODE 1
+/* This option switches the character encoding on the API when LFN is enabled.
+/
+/   0: ANSI/OEM in current CP (TCHAR = char)
+/   1: Unicode in UTF-16 (TCHAR = WCHAR)
+/   2: Unicode in UTF-8 (TCHAR = char)
+/   3: Unicode in UTF-32 (TCHAR = DWORD)
+/
+/  Also behavior of string I/O functions will be affected by this option.
+/  When LFN is not enabled, this option has no effect. */
+
+
+#define FF_LFN_BUF             255
+#define FF_SFN_BUF             12
+/* This set of options defines size of file name members in the FILINFO structure
+/  which is used to read out directory items. These values should be suffcient for
+/  the file names to read. The maximum possible length of the read file name depends
+/  on character encoding. When LFN is not enabled, these options have no effect. */
+
+
+#define FF_STRF_ENCODE 3
+/* When FF_LFN_UNICODE >= 1 with LFN enabled, string I/O functions, f_gets(),
+/  f_putc(), f_puts and f_printf() convert the character encoding in it.
+/  This option selects assumption of character encoding ON THE FILE to be
+/  read/written via those functions.
+/
+/   0: ANSI/OEM in current CP
+/   1: Unicode in UTF-16LE
+/   2: Unicode in UTF-16BE
+/   3: Unicode in UTF-8
+*/
+
+
+#define FF_FS_RPATH            0
+/* This option configures support for relative path.
+/
+/   0: Disable relative path and remove related functions.
+/   1: Enable relative path. f_chdir() and f_chdrive() are available.
+/   2: f_getcwd() function is available in addition to 1.
+*/
+
+
+/*---------------------------------------------------------------------------/
+/ Drive/Volume Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_VOLUMES             1
+/* Number of volumes (logical drives) to be used. (1-10) */
+
+
+#define FF_STR_VOLUME_ID       0
+#define FF_VOLUME_STRS         "Ventoy","Ventoy","Ventoy","SD","SD2","USB","USB2","USB3"
+/* FF_STR_VOLUME_ID switches support for volume ID in arbitrary strings.
+/  When FF_STR_VOLUME_ID is set to 1 or 2, arbitrary strings can be used as drive
+/  number in the path name. FF_VOLUME_STRS defines the volume ID strings for each
+/  logical drives. Number of items must not be less than FF_VOLUMES. Valid
+/  characters for the volume ID strings are A-Z, a-z and 0-9, however, they are
+/  compared in case-insensitive. If FF_STR_VOLUME_ID >= 1 and FF_VOLUME_STRS is
+/  not defined, a user defined volume string table needs to be defined as:
+/
+/  const char* VolumeStr[FF_VOLUMES] = {"ram","flash","sd","usb",...
+*/
+
+
+#define FF_MULTI_PARTITION     0
+/* This option switches support for multiple volumes on the physical drive.
+/  By default (0), each logical drive number is bound to the same physical drive
+/  number and only an FAT volume found on the physical drive will be mounted.
+/  When this function is enabled (1), each logical drive number can be bound to
+/  arbitrary physical drive and partition listed in the VolToPart[]. Also f_fdisk()
+/  funciton will be available. */
+
+
+#define FF_MIN_SS              512
+#define FF_MAX_SS              512
+/* This set of options configures the range of sector size to be supported. (512,
+/  1024, 2048 or 4096) Always set both 512 for most systems, generic memory card and
+/  harddisk. But a larger value may be required for on-board flash memory and some
+/  type of optical media. When FF_MAX_SS is larger than FF_MIN_SS, FatFs is configured
+/  for variable sector size mode and disk_ioctl() function needs to implement
+/  GET_SECTOR_SIZE command. */
+
+
+#define FF_LBA64               1
+/* This option switches support for 64-bit LBA. (0:Disable or 1:Enable)
+/  To enable the 64-bit LBA, also exFAT needs to be enabled. (FF_FS_EXFAT == 1) */
+
+
+#define FF_MIN_GPT             0x100000000
+/* Minimum number of sectors to switch GPT format to create partition in f_mkfs and
+/  f_fdisk function. 0x100000000 max. This option has no effect when FF_LBA64 == 0. */
+
+
+#define FF_USE_TRIM            0
+/* This option switches support for ATA-TRIM. (0:Disable or 1:Enable)
+/  To enable Trim function, also CTRL_TRIM command should be implemented to the
+/  disk_ioctl() function. */
+
+
+
+/*---------------------------------------------------------------------------/
+/ System Configurations
+/---------------------------------------------------------------------------*/
+
+#define FF_FS_TINY             0
+/* This option switches tiny buffer configuration. (0:Normal or 1:Tiny)
+/  At the tiny configuration, size of file object (FIL) is shrinked FF_MAX_SS bytes.
+/  Instead of private sector buffer eliminated from the file object, common sector
+/  buffer in the filesystem object (FATFS) is used for the file data transfer. */
+
+
+#define FF_FS_EXFAT            1
+/* This option switches support for exFAT filesystem. (0:Disable or 1:Enable)
+/  To enable exFAT, also LFN needs to be enabled. (FF_USE_LFN >= 1)
+/  Note that enabling exFAT discards ANSI C (C89) compatibility. */
+
+
+#define FF_FS_NORTC            1
+#define FF_NORTC_MON   1
+#define FF_NORTC_MDAY  1
+#define FF_NORTC_YEAR  2019
+/* The option FF_FS_NORTC switches timestamp functiton. If the system does not have
+/  any RTC function or valid timestamp is not needed, set FF_FS_NORTC = 1 to disable
+/  the timestamp function. Every object modified by FatFs will have a fixed timestamp
+/  defined by FF_NORTC_MON, FF_NORTC_MDAY and FF_NORTC_YEAR in local time.
+/  To enable timestamp function (FF_FS_NORTC = 0), get_fattime() function need to be
+/  added to the project to read current time form real-time clock. FF_NORTC_MON,
+/  FF_NORTC_MDAY and FF_NORTC_YEAR have no effect.
+/  These options have no effect in read-only configuration (FF_FS_READONLY = 1). */
+
+
+#define FF_FS_NOFSINFO 0
+/* If you need to know correct free space on the FAT32 volume, set bit 0 of this
+/  option, and f_getfree() function at first time after volume mount will force
+/  a full FAT scan. Bit 1 controls the use of last allocated cluster number.
+/
+/  bit0=0: Use free cluster count in the FSINFO if available.
+/  bit0=1: Do not trust free cluster count in the FSINFO.
+/  bit1=0: Use last allocated cluster number in the FSINFO if available.
+/  bit1=1: Do not trust last allocated cluster number in the FSINFO.
+*/
+
+
+#define FF_FS_LOCK             0
+/* The option FF_FS_LOCK switches file lock function to control duplicated file open
+/  and illegal operation to open objects. This option must be 0 when FF_FS_READONLY
+/  is 1.
+/
+/  0:  Disable file lock function. To avoid volume corruption, application program
+/      should avoid illegal open, remove and rename to the open objects.
+/  >0: Enable file lock function. The value defines how many files/sub-directories
+/      can be opened simultaneously under file lock control. Note that the file
+/      lock control is independent of re-entrancy. */
+
+
+/* #include <somertos.h>       // O/S definitions */
+#define FF_FS_REENTRANT        0
+#define FF_FS_TIMEOUT  1000
+#define FF_SYNC_t              HANDLE
+/* The option FF_FS_REENTRANT switches the re-entrancy (thread safe) of the FatFs
+/  module itself. Note that regardless of this option, file access to different
+/  volume is always re-entrant and volume control functions, f_mount(), f_mkfs()
+/  and f_fdisk() function, are always not re-entrant. Only file/directory access
+/  to the same volume is under control of this function.
+/
+/   0: Disable re-entrancy. FF_FS_TIMEOUT and FF_SYNC_t have no effect.
+/   1: Enable re-entrancy. Also user provided synchronization handlers,
+/      ff_req_grant(), ff_rel_grant(), ff_del_syncobj() and ff_cre_syncobj()
+/      function, must be added to the project. Samples are available in
+/      option/syscall.c.
+/
+/  The FF_FS_TIMEOUT defines timeout period in unit of time tick.
+/  The FF_SYNC_t defines O/S dependent sync object type. e.g. HANDLE, ID, OS_EVENT*,
+/  SemaphoreHandle_t and etc. A header file for O/S definitions needs to be
+/  included somewhere in the scope of ff.h. */
+
+
+
+/*--- End of configuration options ---*/
diff --git a/Ventoy2Disk/Ventoy2Disk/ff14/source/ffsystem.c b/Ventoy2Disk/Ventoy2Disk/ff14/source/ffsystem.c
new file mode 100644 (file)
index 0000000..227cc52
--- /dev/null
@@ -0,0 +1,171 @@
+/*------------------------------------------------------------------------*/
+/* Sample Code of OS Dependent Functions for FatFs                        */
+/* (C)ChaN, 2018                                                          */
+/*------------------------------------------------------------------------*/
+
+
+#include "ff.h"
+#include <stdlib.h>
+
+
+#if FF_USE_LFN == 3    /* Dynamic memory allocation */
+
+/*------------------------------------------------------------------------*/
+/* Allocate a memory block                                                */
+/*------------------------------------------------------------------------*/
+
+void* ff_memalloc (    /* Returns pointer to the allocated memory block (null if not enough core) */
+       UINT msize              /* Number of bytes to allocate */
+)
+{
+       return malloc(msize);   /* Allocate a new memory block with POSIX API */
+}
+
+
+/*------------------------------------------------------------------------*/
+/* Free a memory block                                                    */
+/*------------------------------------------------------------------------*/
+
+void ff_memfree (
+       void* mblock    /* Pointer to the memory block to free (nothing to do if null) */
+)
+{
+       free(mblock);   /* Free the memory block with POSIX API */
+}
+
+#endif
+
+
+
+#if FF_FS_REENTRANT    /* Mutal exclusion */
+
+/*------------------------------------------------------------------------*/
+/* Create a Synchronization Object                                        */
+/*------------------------------------------------------------------------*/
+/* This function is called in f_mount() function to create a new
+/  synchronization object for the volume, such as semaphore and mutex.
+/  When a 0 is returned, the f_mount() function fails with FR_INT_ERR.
+*/
+
+//const osMutexDef_t Mutex[FF_VOLUMES];        /* Table of CMSIS-RTOS mutex */
+
+
+int ff_cre_syncobj (   /* 1:Function succeeded, 0:Could not create the sync object */
+       BYTE vol,                       /* Corresponding volume (logical drive number) */
+       FF_SYNC_t* sobj         /* Pointer to return the created sync object */
+)
+{
+       /* Win32 */
+       *sobj = CreateMutex(NULL, FALSE, NULL);
+       return (int)(*sobj != INVALID_HANDLE_VALUE);
+
+       /* uITRON */
+//     T_CSEM csem = {TA_TPRI,1,1};
+//     *sobj = acre_sem(&csem);
+//     return (int)(*sobj > 0);
+
+       /* uC/OS-II */
+//     OS_ERR err;
+//     *sobj = OSMutexCreate(0, &err);
+//     return (int)(err == OS_NO_ERR);
+
+       /* FreeRTOS */
+//     *sobj = xSemaphoreCreateMutex();
+//     return (int)(*sobj != NULL);
+
+       /* CMSIS-RTOS */
+//     *sobj = osMutexCreate(&Mutex[vol]);
+//     return (int)(*sobj != NULL);
+}
+
+
+/*------------------------------------------------------------------------*/
+/* Delete a Synchronization Object                                        */
+/*------------------------------------------------------------------------*/
+/* This function is called in f_mount() function to delete a synchronization
+/  object that created with ff_cre_syncobj() function. When a 0 is returned,
+/  the f_mount() function fails with FR_INT_ERR.
+*/
+
+int ff_del_syncobj (   /* 1:Function succeeded, 0:Could not delete due to an error */
+       FF_SYNC_t sobj          /* Sync object tied to the logical drive to be deleted */
+)
+{
+       /* Win32 */
+       return (int)CloseHandle(sobj);
+
+       /* uITRON */
+//     return (int)(del_sem(sobj) == E_OK);
+
+       /* uC/OS-II */
+//     OS_ERR err;
+//     OSMutexDel(sobj, OS_DEL_ALWAYS, &err);
+//     return (int)(err == OS_NO_ERR);
+
+       /* FreeRTOS */
+//  vSemaphoreDelete(sobj);
+//     return 1;
+
+       /* CMSIS-RTOS */
+//     return (int)(osMutexDelete(sobj) == osOK);
+}
+
+
+/*------------------------------------------------------------------------*/
+/* Request Grant to Access the Volume                                     */
+/*------------------------------------------------------------------------*/
+/* This function is called on entering file functions to lock the volume.
+/  When a 0 is returned, the file function fails with FR_TIMEOUT.
+*/
+
+int ff_req_grant (     /* 1:Got a grant to access the volume, 0:Could not get a grant */
+       FF_SYNC_t sobj  /* Sync object to wait */
+)
+{
+       /* Win32 */
+       return (int)(WaitForSingleObject(sobj, FF_FS_TIMEOUT) == WAIT_OBJECT_0);
+
+       /* uITRON */
+//     return (int)(wai_sem(sobj) == E_OK);
+
+       /* uC/OS-II */
+//     OS_ERR err;
+//     OSMutexPend(sobj, FF_FS_TIMEOUT, &err));
+//     return (int)(err == OS_NO_ERR);
+
+       /* FreeRTOS */
+//     return (int)(xSemaphoreTake(sobj, FF_FS_TIMEOUT) == pdTRUE);
+
+       /* CMSIS-RTOS */
+//     return (int)(osMutexWait(sobj, FF_FS_TIMEOUT) == osOK);
+}
+
+
+/*------------------------------------------------------------------------*/
+/* Release Grant to Access the Volume                                     */
+/*------------------------------------------------------------------------*/
+/* This function is called on leaving file functions to unlock the volume.
+*/
+
+void ff_rel_grant (
+       FF_SYNC_t sobj  /* Sync object to be signaled */
+)
+{
+       /* Win32 */
+       ReleaseMutex(sobj);
+
+       /* uITRON */
+//     sig_sem(sobj);
+
+       /* uC/OS-II */
+//     OSMutexPost(sobj);
+
+       /* FreeRTOS */
+//     xSemaphoreGive(sobj);
+
+       /* CMSIS-RTOS */
+//     osMutexRelease(sobj);
+}
+
+#endif
+
diff --git a/Ventoy2Disk/Ventoy2Disk/ff14/source/ffunicode.c b/Ventoy2Disk/Ventoy2Disk/ff14/source/ffunicode.c
new file mode 100644 (file)
index 0000000..3f8e211
--- /dev/null
@@ -0,0 +1,430 @@
+/*------------------------------------------------------------------------*/
+/* Unicode handling functions for FatFs R0.13+                            */
+/*------------------------------------------------------------------------*/
+/* This module will occupy a huge memory in the .const section when the    /
+/  FatFs is configured for LFN with DBCS. If the system has any Unicode    /
+/  utilitiy for the code conversion, this module should be modified to use /
+/  that function to avoid silly memory consumption.                        /
+/-------------------------------------------------------------------------*/
+/*
+/ Copyright (C) 2014, ChaN, all right reserved.
+/
+/ FatFs module is an open source software. Redistribution and use of FatFs in
+/ source and binary forms, with or without modification, are permitted provided
+/ that the following condition is met:
+/
+/ 1. Redistributions of source code must retain the above copyright notice,
+/    this condition and the following disclaimer.
+/
+/ This software is provided by the copyright holder and contributors "AS IS"
+/ and any warranties related to this software are DISCLAIMED.
+/ The copyright owner or contributors be NOT LIABLE for any damages caused
+/ by use of this software.
+*/
+
+
+#include "ff.h"
+
+#if FF_USE_LFN /* This module will be blanked if non-LFN configuration */
+
+#define MERGE2(a, b) a ## b
+#define CVTBL(tbl, cp) MERGE2(tbl, cp)
+
+
+/*------------------------------------------------------------------------*/
+/* Code Conversion Tables                                                 */
+/*------------------------------------------------------------------------*/
+
+#if FF_CODE_PAGE == 437 || FF_CODE_PAGE == 0
+static const WCHAR uc437[] = { /*  CP437(U.S.) to Unicode conversion table */
+       0x00C7, 0x00FC, 0x00E9, 0x00E2, 0x00E4, 0x00E0, 0x00E5, 0x00E7, 0x00EA, 0x00EB, 0x00E8, 0x00EF, 0x00EE, 0x00EC, 0x00C4, 0x00C5,
+       0x00C9, 0x00E6, 0x00C6, 0x00F4, 0x00F6, 0x00F2, 0x00FB, 0x00F9, 0x00FF, 0x00D6, 0x00DC, 0x00A2, 0x00A3, 0x00A5, 0x20A7, 0x0192,
+       0x00E1, 0x00ED, 0x00F3, 0x00FA, 0x00F1, 0x00D1, 0x00AA, 0x00BA, 0x00BF, 0x2310, 0x00AC, 0x00BD, 0x00BC, 0x00A1, 0x00AB, 0x00BB,
+       0x2591, 0x2592, 0x2593, 0x2502, 0x2524, 0x2561, 0x2562, 0x2556, 0x2555, 0x2563, 0x2551, 0x2557, 0x255D, 0x255C, 0x255B, 0x2510,
+       0x2514, 0x2534, 0x252C, 0x251C, 0x2500, 0x253C, 0x255E, 0x255F, 0x255A, 0x2554, 0x2569, 0x2566, 0x2560, 0x2550, 0x256C, 0x2567,
+       0x2568, 0x2564, 0x2565, 0x2559, 0x2558, 0x2552, 0x2553, 0x256B, 0x256A, 0x2518, 0x250C, 0x2588, 0x2584, 0x258C, 0x2590, 0x2580,
+       0x03B1, 0x00DF, 0x0393, 0x03C0, 0x03A3, 0x03C3, 0x00B5, 0x03C4, 0x03A6, 0x0398, 0x03A9, 0x03B4, 0x221E, 0x03C6, 0x03B5, 0x2229,
+       0x2261, 0x00B1, 0x2265, 0x2264, 0x2320, 0x2321, 0x00F7, 0x2248, 0x00B0, 0x2219, 0x00B7, 0x221A, 0x207F, 0x00B2, 0x25A0, 0x00A0
+};
+#endif
+
+
+
+/*------------------------------------------------------------------------*/
+/* OEM <==> Unicode conversions for static code page configuration        */
+/* SBCS fixed code page                                                   */
+/*------------------------------------------------------------------------*/
+
+#if FF_CODE_PAGE != 0 && FF_CODE_PAGE < 900
+WCHAR ff_uni2oem (     /* Returns OEM code character, zero on error */
+       DWORD   uni,    /* UTF-16 encoded character to be converted */
+       WORD    cp              /* Code page for the conversion */
+)
+{
+       WCHAR c = 0;
+       const WCHAR *p = CVTBL(uc, FF_CODE_PAGE);
+
+
+       if (uni < 0x80) {       /* ASCII? */
+               c = (WCHAR)uni;
+
+       } else {                        /* Non-ASCII */
+               if (uni < 0x10000 && cp == FF_CODE_PAGE) {      /* Is it in BMP and valid code page? */
+                       for (c = 0; c < 0x80 && uni != p[c]; c++) ;
+                       c = (c + 0x80) & 0xFF;
+               }
+       }
+
+       return c;
+}
+
+WCHAR ff_oem2uni (     /* Returns Unicode character in UTF-16, zero on error */
+       WCHAR   oem,    /* OEM code to be converted */
+       WORD    cp              /* Code page for the conversion */
+)
+{
+       WCHAR c = 0;
+       const WCHAR *p = CVTBL(uc, FF_CODE_PAGE);
+
+
+       if (oem < 0x80) {       /* ASCII? */
+               c = oem;
+
+       } else {                        /* Extended char */
+               if (cp == FF_CODE_PAGE) {       /* Is it a valid code page? */
+                       if (oem < 0x100) c = p[oem - 0x80];
+               }
+       }
+
+       return c;
+}
+
+#endif
+
+
+
+/*------------------------------------------------------------------------*/
+/* OEM <==> Unicode conversions for static code page configuration        */
+/* DBCS fixed code page                                                   */
+/*------------------------------------------------------------------------*/
+
+#if FF_CODE_PAGE >= 900
+WCHAR ff_uni2oem (     /* Returns OEM code character, zero on error */
+       DWORD   uni,    /* UTF-16 encoded character to be converted */
+       WORD    cp              /* Code page for the conversion */
+)
+{
+       const WCHAR *p;
+       WCHAR c = 0, uc;
+       UINT i = 0, n, li, hi;
+
+
+       if (uni < 0x80) {       /* ASCII? */
+               c = (WCHAR)uni;
+
+       } else {                        /* Non-ASCII */
+               if (uni < 0x10000 && cp == FF_CODE_PAGE) {      /* Is it in BMP and valid code page? */
+                       uc = (WCHAR)uni;
+                       p = CVTBL(uni2oem, FF_CODE_PAGE);
+                       hi = sizeof CVTBL(uni2oem, FF_CODE_PAGE) / 4 - 1;
+                       li = 0;
+                       for (n = 16; n; n--) {
+                               i = li + (hi - li) / 2;
+                               if (uc == p[i * 2]) break;
+                               if (uc > p[i * 2]) {
+                                       li = i;
+                               } else {
+                                       hi = i;
+                               }
+                       }
+                       if (n != 0) c = p[i * 2 + 1];
+               }
+       }
+
+       return c;
+}
+
+
+WCHAR ff_oem2uni (     /* Returns Unicode character in UTF-16, zero on error */
+       WCHAR   oem,    /* OEM code to be converted */
+       WORD    cp              /* Code page for the conversion */
+)
+{
+       const WCHAR *p;
+       WCHAR c = 0;
+       UINT i = 0, n, li, hi;
+
+
+       if (oem < 0x80) {       /* ASCII? */
+               c = oem;
+
+       } else {                        /* Extended char */
+               if (cp == FF_CODE_PAGE) {       /* Is it valid code page? */
+                       p = CVTBL(oem2uni, FF_CODE_PAGE);
+                       hi = sizeof CVTBL(oem2uni, FF_CODE_PAGE) / 4 - 1;
+                       li = 0;
+                       for (n = 16; n; n--) {
+                               i = li + (hi - li) / 2;
+                               if (oem == p[i * 2]) break;
+                               if (oem > p[i * 2]) {
+                                       li = i;
+                               } else {
+                                       hi = i;
+                               }
+                       }
+                       if (n != 0) c = p[i * 2 + 1];
+               }
+       }
+
+       return c;
+}
+#endif
+
+
+
+/*------------------------------------------------------------------------*/
+/* OEM <==> Unicode conversions for dynamic code page configuration       */
+/*------------------------------------------------------------------------*/
+
+#if FF_CODE_PAGE == 0
+
+static const WORD cp_code[]          = {  437,   720,   737,   771,   775,   850,   852,   855,   857,   860,   861,   862,   863,   864,   865,   866,   869, 0};
+static const WCHAR* const cp_table[] = {uc437, uc720, uc737, uc771, uc775, uc850, uc852, uc855, uc857, uc860, uc861, uc862, uc863, uc864, uc865, uc866, uc869, 0};
+
+
+WCHAR ff_uni2oem (     /* Returns OEM code character, zero on error */
+       DWORD   uni,    /* UTF-16 encoded character to be converted */
+       WORD    cp              /* Code page for the conversion */
+)
+{
+       const WCHAR *p;
+       WCHAR c = 0, uc;
+       UINT i, n, li, hi;
+
+
+       if (uni < 0x80) {       /* ASCII? */
+               c = (WCHAR)uni;
+
+       } else {                        /* Non-ASCII */
+               if (uni < 0x10000) { /* Is it in BMP? */
+                       uc = (WCHAR)uni;
+                       p = 0;
+                       if (cp < 900) { /* SBCS */
+                               for (i = 0; cp_code[i] != 0 && cp_code[i] != cp; i++) ;         /* Get conversion table */
+                               p = cp_table[i];
+                               if (p) {        /* Is it valid code page ? */
+                                       for (c = 0; c < 0x80 && uc != p[c]; c++) ;      /* Find OEM code in the table */
+                                       c = (c + 0x80) & 0xFF;
+                               }
+                       } else {        /* DBCS */
+                               switch (cp) {   /* Get conversion table */
+                               case 932 : p = uni2oem932; hi = sizeof uni2oem932 / 4 - 1; break;
+                               case 936 : p = uni2oem936; hi = sizeof uni2oem936 / 4 - 1; break;
+                               case 949 : p = uni2oem949; hi = sizeof uni2oem949 / 4 - 1; break;
+                               case 950 : p = uni2oem950; hi = sizeof uni2oem950 / 4 - 1; break;
+                               }
+                               if (p) {        /* Is it valid code page? */
+                                       li = 0;
+                                       for (n = 16; n; n--) {  /* Find OEM code */
+                                               i = li + (hi - li) / 2;
+                                               if (uc == p[i * 2]) break;
+                                               if (uc > p[i * 2]) {
+                                                       li = i;
+                                               } else {
+                                                       hi = i;
+                                               }
+                                       }
+                                       if (n != 0) c = p[i * 2 + 1];
+                               }
+                       }
+               }
+       }
+
+       return c;
+}
+
+
+WCHAR ff_oem2uni (     /* Returns Unicode character in UTF-16, zero on error */
+       WCHAR   oem,    /* OEM code to be converted (DBC if >=0x100) */
+       WORD    cp              /* Code page for the conversion */
+)
+{
+       const WCHAR *p;
+       WCHAR c = 0;
+       UINT i, n, li, hi;
+
+
+       if (oem < 0x80) {       /* ASCII? */
+               c = oem;
+
+       } else {                        /* Extended char */
+               p = 0;
+               if (cp < 900) { /* SBCS */
+                       for (i = 0; cp_code[i] != 0 && cp_code[i] != cp; i++) ;         /* Get table */
+                       p = cp_table[i];
+                       if (p) {        /* Is it a valid CP ? */
+                               if (oem < 0x100) c = p[oem - 0x80];
+                       }
+               } else {        /* DBCS */
+                       switch (cp) {
+                       case 932 : p = oem2uni932; hi = sizeof oem2uni932 / 4 - 1; break;
+                       case 936 : p = oem2uni936; hi = sizeof oem2uni936 / 4 - 1; break;
+                       case 949 : p = oem2uni949; hi = sizeof oem2uni949 / 4 - 1; break;
+                       case 950 : p = oem2uni950; hi = sizeof oem2uni950 / 4 - 1; break;
+                       }
+                       if (p) {
+                               li = 0;
+                               for (n = 16; n; n--) {
+                                       i = li + (hi - li) / 2;
+                                       if (oem == p[i * 2]) break;
+                                       if (oem > p[i * 2]) {
+                                               li = i;
+                                       } else {
+                                               hi = i;
+                                       }
+                               }
+                               if (n != 0) c = p[i * 2 + 1];
+                       }
+               }
+       }
+
+       return c;
+}
+#endif
+
+
+
+/*------------------------------------------------------------------------*/
+/* Unicode up-case conversion                                             */
+/*------------------------------------------------------------------------*/
+
+DWORD ff_wtoupper (    /* Returns up-converted code point */
+       DWORD uni               /* Unicode code point to be up-converted */
+)
+{
+       const WORD *p;
+       WORD uc, bc, nc, cmd;
+       static const WORD cvt1[] = {    /* Compressed up conversion table for U+0000 - U+0FFF */
+               /* Basic Latin */
+               0x0061,0x031A,
+               /* Latin-1 Supplement */
+               0x00E0,0x0317,
+               0x00F8,0x0307,
+               0x00FF,0x0001,0x0178,
+               /* Latin Extended-A */
+               0x0100,0x0130,
+               0x0132,0x0106,
+               0x0139,0x0110,
+               0x014A,0x012E,
+               0x0179,0x0106,
+               /* Latin Extended-B */
+               0x0180,0x004D,0x0243,0x0181,0x0182,0x0182,0x0184,0x0184,0x0186,0x0187,0x0187,0x0189,0x018A,0x018B,0x018B,0x018D,0x018E,0x018F,0x0190,0x0191,0x0191,0x0193,0x0194,0x01F6,0x0196,0x0197,0x0198,0x0198,0x023D,0x019B,0x019C,0x019D,0x0220,0x019F,0x01A0,0x01A0,0x01A2,0x01A2,0x01A4,0x01A4,0x01A6,0x01A7,0x01A7,0x01A9,0x01AA,0x01AB,0x01AC,0x01AC,0x01AE,0x01AF,0x01AF,0x01B1,0x01B2,0x01B3,0x01B3,0x01B5,0x01B5,0x01B7,0x01B8,0x01B8,0x01BA,0x01BB,0x01BC,0x01BC,0x01BE,0x01F7,0x01C0,0x01C1,0x01C2,0x01C3,0x01C4,0x01C5,0x01C4,0x01C7,0x01C8,0x01C7,0x01CA,0x01CB,0x01CA,
+               0x01CD,0x0110,
+               0x01DD,0x0001,0x018E,
+               0x01DE,0x0112,
+               0x01F3,0x0003,0x01F1,0x01F4,0x01F4,
+               0x01F8,0x0128,
+               0x0222,0x0112,
+               0x023A,0x0009,0x2C65,0x023B,0x023B,0x023D,0x2C66,0x023F,0x0240,0x0241,0x0241,
+               0x0246,0x010A,
+               /* IPA Extensions */
+               0x0253,0x0040,0x0181,0x0186,0x0255,0x0189,0x018A,0x0258,0x018F,0x025A,0x0190,0x025C,0x025D,0x025E,0x025F,0x0193,0x0261,0x0262,0x0194,0x0264,0x0265,0x0266,0x0267,0x0197,0x0196,0x026A,0x2C62,0x026C,0x026D,0x026E,0x019C,0x0270,0x0271,0x019D,0x0273,0x0274,0x019F,0x0276,0x0277,0x0278,0x0279,0x027A,0x027B,0x027C,0x2C64,0x027E,0x027F,0x01A6,0x0281,0x0282,0x01A9,0x0284,0x0285,0x0286,0x0287,0x01AE,0x0244,0x01B1,0x01B2,0x0245,0x028D,0x028E,0x028F,0x0290,0x0291,0x01B7,
+               /* Greek, Coptic */
+               0x037B,0x0003,0x03FD,0x03FE,0x03FF,
+               0x03AC,0x0004,0x0386,0x0388,0x0389,0x038A,
+               0x03B1,0x0311,
+               0x03C2,0x0002,0x03A3,0x03A3,
+               0x03C4,0x0308,
+               0x03CC,0x0003,0x038C,0x038E,0x038F,
+               0x03D8,0x0118,
+               0x03F2,0x000A,0x03F9,0x03F3,0x03F4,0x03F5,0x03F6,0x03F7,0x03F7,0x03F9,0x03FA,0x03FA,
+               /* Cyrillic */
+               0x0430,0x0320,
+               0x0450,0x0710,
+               0x0460,0x0122,
+               0x048A,0x0136,
+               0x04C1,0x010E,
+               0x04CF,0x0001,0x04C0,
+               0x04D0,0x0144,
+               /* Armenian */
+               0x0561,0x0426,
+
+               0x0000  /* EOT */
+       };
+       static const WORD cvt2[] = {    /* Compressed up conversion table for U+1000 - U+FFFF */
+               /* Phonetic Extensions */
+               0x1D7D,0x0001,0x2C63,
+               /* Latin Extended Additional */
+               0x1E00,0x0196,
+               0x1EA0,0x015A,
+               /* Greek Extended */
+               0x1F00,0x0608,
+               0x1F10,0x0606,
+               0x1F20,0x0608,
+               0x1F30,0x0608,
+               0x1F40,0x0606,
+               0x1F51,0x0007,0x1F59,0x1F52,0x1F5B,0x1F54,0x1F5D,0x1F56,0x1F5F,
+               0x1F60,0x0608,
+               0x1F70,0x000E,0x1FBA,0x1FBB,0x1FC8,0x1FC9,0x1FCA,0x1FCB,0x1FDA,0x1FDB,0x1FF8,0x1FF9,0x1FEA,0x1FEB,0x1FFA,0x1FFB,
+               0x1F80,0x0608,
+               0x1F90,0x0608,
+               0x1FA0,0x0608,
+               0x1FB0,0x0004,0x1FB8,0x1FB9,0x1FB2,0x1FBC,
+               0x1FCC,0x0001,0x1FC3,
+               0x1FD0,0x0602,
+               0x1FE0,0x0602,
+               0x1FE5,0x0001,0x1FEC,
+               0x1FF3,0x0001,0x1FFC,
+               /* Letterlike Symbols */
+               0x214E,0x0001,0x2132,
+               /* Number forms */
+               0x2170,0x0210,
+               0x2184,0x0001,0x2183,
+               /* Enclosed Alphanumerics */
+               0x24D0,0x051A,
+               0x2C30,0x042F,
+               /* Latin Extended-C */
+               0x2C60,0x0102,
+               0x2C67,0x0106, 0x2C75,0x0102,
+               /* Coptic */
+               0x2C80,0x0164,
+               /* Georgian Supplement */
+               0x2D00,0x0826,
+               /* Full-width */
+               0xFF41,0x031A,
+
+               0x0000  /* EOT */
+       };
+
+
+       if (uni < 0x10000) {    /* Is it in BMP? */
+               uc = (WORD)uni;
+               p = uc < 0x1000 ? cvt1 : cvt2;
+               for (;;) {
+                       bc = *p++;                                                              /* Get the block base */
+                       if (bc == 0 || uc < bc) break;                  /* Not matched? */
+                       nc = *p++; cmd = nc >> 8; nc &= 0xFF;   /* Get processing command and block size */
+                       if (uc < bc + nc) {     /* In the block? */
+                               switch (cmd) {
+                               case 0: uc = p[uc - bc]; break;         /* Table conversion */
+                               case 1: uc -= (uc - bc) & 1; break;     /* Case pairs */
+                               case 2: uc -= 16; break;                        /* Shift -16 */
+                               case 3: uc -= 32; break;                        /* Shift -32 */
+                               case 4: uc -= 48; break;                        /* Shift -48 */
+                               case 5: uc -= 26; break;                        /* Shift -26 */
+                               case 6: uc += 8; break;                         /* Shift +8 */
+                               case 7: uc -= 80; break;                        /* Shift -80 */
+                               case 8: uc -= 0x1C60; break;            /* Shift -0x1C60 */
+                               }
+                               break;
+                       }
+                       if (cmd == 0) p += nc;  /* Skip table if needed */
+               }
+               uni = uc;
+       }
+
+       return uni;
+}
+
+
+#endif /* #if FF_USE_LFN */
diff --git a/Ventoy2Disk/Ventoy2Disk/resource.h b/Ventoy2Disk/Ventoy2Disk/resource.h
new file mode 100644 (file)
index 0000000..4e81d7f
Binary files /dev/null and b/Ventoy2Disk/Ventoy2Disk/resource.h differ
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/COPYING b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/COPYING
new file mode 100644 (file)
index 0000000..fc4fbf7
--- /dev/null
@@ -0,0 +1,10 @@
+
+Licensing of XZ Embedded
+========================
+
+    All the files in this package have been written by Lasse Collin
+    and/or Igor Pavlov. All these files have been put into the
+    public domain. You can do whatever you want with these files.
+
+    As usual, this software is provided "as is", without any warranty.
+
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/README b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/README
new file mode 100644 (file)
index 0000000..566d604
--- /dev/null
@@ -0,0 +1,163 @@
+
+XZ Embedded
+===========
+
+    XZ Embedded is a relatively small, limited implementation of the .xz
+    file format. Currently only decoding is implemented.
+
+    XZ Embedded was written for use in the Linux kernel, but the code can
+    be easily used in other environments too, including regular userspace
+    applications. See userspace/xzminidec.c for an example program.
+
+    This README contains information that is useful only when the copy
+    of XZ Embedded isn't part of the Linux kernel tree. You should also
+    read linux/Documentation/xz.txt even if you aren't using XZ Embedded
+    as part of Linux; information in that file is not repeated in this
+    README.
+
+Compiling the Linux kernel module
+
+    The xz_dec module depends on crc32 module, so make sure that you have
+    it enabled (CONFIG_CRC32).
+
+    Building the xz_dec and xz_dec_test modules without support for BCJ
+    filters:
+
+        cd linux/lib/xz
+        make -C /path/to/kernel/source \
+                KCPPFLAGS=-I"$(pwd)/../../include" M="$(pwd)" \
+                CONFIG_XZ_DEC=m CONFIG_XZ_DEC_TEST=m
+
+    Building the xz_dec and xz_dec_test modules with support for BCJ
+    filters:
+
+        cd linux/lib/xz
+        make -C /path/to/kernel/source \
+                KCPPFLAGS=-I"$(pwd)/../../include" M="$(pwd)" \
+                CONFIG_XZ_DEC=m CONFIG_XZ_DEC_TEST=m CONFIG_XZ_DEC_BCJ=y \
+                CONFIG_XZ_DEC_X86=y CONFIG_XZ_DEC_POWERPC=y \
+                CONFIG_XZ_DEC_IA64=y CONFIG_XZ_DEC_ARM=y \
+                CONFIG_XZ_DEC_ARMTHUMB=y CONFIG_XZ_DEC_SPARC=y
+
+    If you want only one or a few of the BCJ filters, omit the appropriate
+    variables. CONFIG_XZ_DEC_BCJ=y is always required to build the support
+    code shared between all BCJ filters.
+
+    Most people don't need the xz_dec_test module. You can skip building
+    it by omitting CONFIG_XZ_DEC_TEST=m from the make command line.
+
+Compiler requirements
+
+    XZ Embedded should compile as either GNU-C89 (used in the Linux
+    kernel) or with any C99 compiler. Getting the code to compile with
+    non-GNU C89 compiler or a C++ compiler should be quite easy as
+    long as there is a data type for unsigned 64-bit integer (or the
+    code is modified not to support large files, which needs some more
+    care than just using 32-bit integer instead of 64-bit).
+
+    If you use GCC, try to use a recent version. For example, on x86-32,
+    xz_dec_lzma2.c compiled with GCC 3.3.6 is 15-25 % slower than when
+    compiled with GCC 4.3.3.
+
+Embedding into userspace applications
+
+    To embed the XZ decoder, copy the following files into a single
+    directory in your source code tree:
+
+        linux/include/linux/xz.h
+        linux/lib/xz/xz_crc32.c
+        linux/lib/xz/xz_dec_lzma2.c
+        linux/lib/xz/xz_dec_stream.c
+        linux/lib/xz/xz_lzma2.h
+        linux/lib/xz/xz_private.h
+        linux/lib/xz/xz_stream.h
+        userspace/xz_config.h
+
+    Alternatively, xz.h may be placed into a different directory but then
+    that directory must be in the compiler include path when compiling
+    the .c files.
+
+    Your code should use only the functions declared in xz.h. The rest of
+    the .h files are meant only for internal use in XZ Embedded.
+
+    You may want to modify xz_config.h to be more suitable for your build
+    environment. Probably you should at least skim through it even if the
+    default file works as is.
+
+Integrity check support
+
+    XZ Embedded always supports the integrity check types None and
+    CRC32. Support for CRC64 is optional. SHA-256 is currently not
+    supported in XZ Embedded although the .xz format does support it.
+    The xz tool from XZ Utils uses CRC64 by default, but CRC32 is usually
+    enough in embedded systems to keep the code size smaller.
+
+    If you want support for CRC64, you need to copy linux/lib/xz/xz_crc64.c
+    into your application, and #define XZ_USE_CRC64 in xz_config.h or in
+    compiler flags.
+
+    When using the internal CRC32 or CRC64, their lookup tables need to be
+    initialized with xz_crc32_init() and xz_crc64_init(), respectively.
+    See xz.h for details.
+
+    To use external CRC32 or CRC64 code instead of the code from
+    xz_crc32.c or xz_crc64.c, the following #defines may be used
+    in xz_config.h or in compiler flags:
+
+        #define XZ_INTERNAL_CRC32 0
+        #define XZ_INTERNAL_CRC64 0
+
+    Then it is up to you to provide compatible xz_crc32() or xz_crc64()
+    functions.
+
+    If the .xz file being decompressed uses an integrity check type that
+    isn't supported by XZ Embedded, it is treated as an error and the
+    file cannot be decompressed. For multi-call mode, this can be modified
+    by #defining XZ_DEC_ANY_CHECK. Then xz_dec_run() will return
+    XZ_UNSUPPORTED_CHECK when unsupported check type is detected. After
+    that decompression can be continued normally except that the
+    integrity check won't be verified. In single-call mode there's
+    no way to continue decoding, so XZ_DEC_ANY_CHECK is almost useless
+    in single-call mode.
+
+BCJ filter support
+
+    If you want support for one or more BCJ filters, you need to copy also
+    linux/lib/xz/xz_dec_bcj.c into your application, and use appropriate
+    #defines in xz_config.h or in compiler flags. You don't need these
+    #defines in the code that just uses XZ Embedded via xz.h, but having
+    them always #defined doesn't hurt either.
+
+        #define             Instruction set     BCJ filter endianness
+        XZ_DEC_X86          x86-32 or x86-64    Little endian only
+        XZ_DEC_POWERPC      PowerPC             Big endian only
+        XZ_DEC_IA64         Itanium (IA-64)     Big or little endian
+        XZ_DEC_ARM          ARM                 Little endian only
+        XZ_DEC_ARMTHUMB     ARM-Thumb           Little endian only
+        XZ_DEC_SPARC        SPARC               Big or little endian
+
+    While some architectures are (partially) bi-endian, the endianness
+    setting doesn't change the endianness of the instructions on all
+    architectures. That's why Itanium and SPARC filters work for both big
+    and little endian executables (Itanium has little endian instructions
+    and SPARC has big endian instructions).
+
+    There currently is no filter for little endian PowerPC or big endian
+    ARM or ARM-Thumb. Implementing filters for them can be considered if
+    there is a need for such filters in real-world applications.
+
+Notes about shared libraries
+
+    If you are including XZ Embedded into a shared library, you very
+    probably should rename the xz_* functions to prevent symbol
+    conflicts in case your library is linked against some other library
+    or application that also has XZ Embedded in it (which may even be
+    a different version of XZ Embedded). TODO: Provide an easy way
+    to do this.
+
+    Please don't create a shared library of XZ Embedded itself unless
+    it is fine to rebuild everything depending on that shared library
+    everytime you upgrade to a newer version of XZ Embedded. There are
+    no API or ABI stability guarantees between different versions of
+    XZ Embedded.
+
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/Documentation/xz.txt b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/Documentation/xz.txt
new file mode 100644 (file)
index 0000000..68329ac
--- /dev/null
@@ -0,0 +1,122 @@
+
+XZ data compression in Linux
+============================
+
+Introduction
+
+    XZ is a general purpose data compression format with high compression
+    ratio and relatively fast decompression. The primary compression
+    algorithm (filter) is LZMA2. Additional filters can be used to improve
+    compression ratio even further. E.g. Branch/Call/Jump (BCJ) filters
+    improve compression ratio of executable data.
+
+    The XZ decompressor in Linux is called XZ Embedded. It supports
+    the LZMA2 filter and optionally also BCJ filters. CRC32 is supported
+    for integrity checking. The home page of XZ Embedded is at
+    <http://tukaani.org/xz/embedded.html>, where you can find the
+    latest version and also information about using the code outside
+    the Linux kernel.
+
+    For userspace, XZ Utils provide a zlib-like compression library
+    and a gzip-like command line tool. XZ Utils can be downloaded from
+    <http://tukaani.org/xz/>.
+
+XZ related components in the kernel
+
+    The xz_dec module provides XZ decompressor with single-call (buffer
+    to buffer) and multi-call (stateful) APIs. The usage of the xz_dec
+    module is documented in include/linux/xz.h.
+
+    The xz_dec_test module is for testing xz_dec. xz_dec_test is not
+    useful unless you are hacking the XZ decompressor. xz_dec_test
+    allocates a char device major dynamically to which one can write
+    .xz files from userspace. The decompressed output is thrown away.
+    Keep an eye on dmesg to see diagnostics printed by xz_dec_test.
+    See the xz_dec_test source code for the details.
+
+    For decompressing the kernel image, initramfs, and initrd, there
+    is a wrapper function in lib/decompress_unxz.c. Its API is the
+    same as in other decompress_*.c files, which is defined in
+    include/linux/decompress/generic.h.
+
+    scripts/xz_wrap.sh is a wrapper for the xz command line tool found
+    from XZ Utils. The wrapper sets compression options to values suitable
+    for compressing the kernel image.
+
+    For kernel makefiles, two commands are provided for use with
+    $(call if_needed). The kernel image should be compressed with
+    $(call if_needed,xzkern) which will use a BCJ filter and a big LZMA2
+    dictionary. It will also append a four-byte trailer containing the
+    uncompressed size of the file, which is needed by the boot code.
+    Other things should be compressed with $(call if_needed,xzmisc)
+    which will use no BCJ filter and 1 MiB LZMA2 dictionary.
+
+Notes on compression options
+
+    Since the XZ Embedded supports only streams with no integrity check or
+    CRC32, make sure that you don't use some other integrity check type
+    when encoding files that are supposed to be decoded by the kernel. With
+    liblzma, you need to use either LZMA_CHECK_NONE or LZMA_CHECK_CRC32
+    when encoding. With the xz command line tool, use --check=none or
+    --check=crc32.
+
+    Using CRC32 is strongly recommended unless there is some other layer
+    which will verify the integrity of the uncompressed data anyway.
+    Double checking the integrity would probably be waste of CPU cycles.
+    Note that the headers will always have a CRC32 which will be validated
+    by the decoder; you can only change the integrity check type (or
+    disable it) for the actual uncompressed data.
+
+    In userspace, LZMA2 is typically used with dictionary sizes of several
+    megabytes. The decoder needs to have the dictionary in RAM, thus big
+    dictionaries cannot be used for files that are intended to be decoded
+    by the kernel. 1 MiB is probably the maximum reasonable dictionary
+    size for in-kernel use (maybe more is OK for initramfs). The presets
+    in XZ Utils may not be optimal when creating files for the kernel,
+    so don't hesitate to use custom settings. Example:
+
+        xz --check=crc32 --lzma2=dict=512KiB inputfile
+
+    An exception to above dictionary size limitation is when the decoder
+    is used in single-call mode. Decompressing the kernel itself is an
+    example of this situation. In single-call mode, the memory usage
+    doesn't depend on the dictionary size, and it is perfectly fine to
+    use a big dictionary: for maximum compression, the dictionary should
+    be at least as big as the uncompressed data itself.
+
+Future plans
+
+    Creating a limited XZ encoder may be considered if people think it is
+    useful. LZMA2 is slower to compress than e.g. Deflate or LZO even at
+    the fastest settings, so it isn't clear if LZMA2 encoder is wanted
+    into the kernel.
+
+    Support for limited random-access reading is planned for the
+    decompression code. I don't know if it could have any use in the
+    kernel, but I know that it would be useful in some embedded projects
+    outside the Linux kernel.
+
+Conformance to the .xz file format specification
+
+    There are a couple of corner cases where things have been simplified
+    at expense of detecting errors as early as possible. These should not
+    matter in practice all, since they don't cause security issues. But
+    it is good to know this if testing the code e.g. with the test files
+    from XZ Utils.
+
+Reporting bugs
+
+    Before reporting a bug, please check that it's not fixed already
+    at upstream. See <http://tukaani.org/xz/embedded.html> to get the
+    latest code.
+
+    Report bugs to <lasse.collin@tukaani.org> or visit #tukaani on
+    Freenode and talk to Larhzu. I don't actively read LKML or other
+    kernel-related mailing lists, so if there's something I should know,
+    you should email to me personally or use IRC.
+
+    Don't bother Igor Pavlov with questions about the XZ implementation
+    in the kernel or about XZ Utils. While these two implementations
+    include essential code that is directly based on Igor Pavlov's code,
+    these implementations aren't maintained nor supported by him.
+
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/include/linux/decompress/unxz.h b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/include/linux/decompress/unxz.h
new file mode 100644 (file)
index 0000000..41728fc
--- /dev/null
@@ -0,0 +1,19 @@
+/*
+ * Wrapper for decompressing XZ-compressed kernel, initramfs, and initrd
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef DECOMPRESS_UNXZ_H
+#define DECOMPRESS_UNXZ_H
+
+int unxz(unsigned char *in, int in_size,
+        int (*fill)(void *dest, unsigned int size),
+        int (*flush)(void *src, unsigned int size),
+        unsigned char *out, int *in_used,
+        void (*error)(char *x));
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/include/linux/xz.h b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/include/linux/xz.h
new file mode 100644 (file)
index 0000000..0a4b38d
--- /dev/null
@@ -0,0 +1,304 @@
+/*
+ * XZ decompressor
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ *          Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_H
+#define XZ_H
+
+#ifdef __KERNEL__
+#      include <linux/stddef.h>
+#      include <linux/types.h>
+#else
+#      include <stddef.h>
+#      include <stdint.h>
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/* In Linux, this is used to make extern functions static when needed. */
+#ifndef XZ_EXTERN
+#      define XZ_EXTERN extern
+#endif
+
+/**
+ * enum xz_mode - Operation mode
+ *
+ * @XZ_SINGLE:              Single-call mode. This uses less RAM than
+ *                          than multi-call modes, because the LZMA2
+ *                          dictionary doesn't need to be allocated as
+ *                          part of the decoder state. All required data
+ *                          structures are allocated at initialization,
+ *                          so xz_dec_run() cannot return XZ_MEM_ERROR.
+ * @XZ_PREALLOC:            Multi-call mode with preallocated LZMA2
+ *                          dictionary buffer. All data structures are
+ *                          allocated at initialization, so xz_dec_run()
+ *                          cannot return XZ_MEM_ERROR.
+ * @XZ_DYNALLOC:            Multi-call mode. The LZMA2 dictionary is
+ *                          allocated once the required size has been
+ *                          parsed from the stream headers. If the
+ *                          allocation fails, xz_dec_run() will return
+ *                          XZ_MEM_ERROR.
+ *
+ * It is possible to enable support only for a subset of the above
+ * modes at compile time by defining XZ_DEC_SINGLE, XZ_DEC_PREALLOC,
+ * or XZ_DEC_DYNALLOC. The xz_dec kernel module is always compiled
+ * with support for all operation modes, but the preboot code may
+ * be built with fewer features to minimize code size.
+ */
+enum xz_mode {
+       XZ_SINGLE,
+       XZ_PREALLOC,
+       XZ_DYNALLOC
+};
+
+/**
+ * enum xz_ret - Return codes
+ * @XZ_OK:                  Everything is OK so far. More input or more
+ *                          output space is required to continue. This
+ *                          return code is possible only in multi-call mode
+ *                          (XZ_PREALLOC or XZ_DYNALLOC).
+ * @XZ_STREAM_END:          Operation finished successfully.
+ * @XZ_UNSUPPORTED_CHECK:   Integrity check type is not supported. Decoding
+ *                          is still possible in multi-call mode by simply
+ *                          calling xz_dec_run() again.
+ *                          Note that this return value is used only if
+ *                          XZ_DEC_ANY_CHECK was defined at build time,
+ *                          which is not used in the kernel. Unsupported
+ *                          check types return XZ_OPTIONS_ERROR if
+ *                          XZ_DEC_ANY_CHECK was not defined at build time.
+ * @XZ_MEM_ERROR:           Allocating memory failed. This return code is
+ *                          possible only if the decoder was initialized
+ *                          with XZ_DYNALLOC. The amount of memory that was
+ *                          tried to be allocated was no more than the
+ *                          dict_max argument given to xz_dec_init().
+ * @XZ_MEMLIMIT_ERROR:      A bigger LZMA2 dictionary would be needed than
+ *                          allowed by the dict_max argument given to
+ *                          xz_dec_init(). This return value is possible
+ *                          only in multi-call mode (XZ_PREALLOC or
+ *                          XZ_DYNALLOC); the single-call mode (XZ_SINGLE)
+ *                          ignores the dict_max argument.
+ * @XZ_FORMAT_ERROR:        File format was not recognized (wrong magic
+ *                          bytes).
+ * @XZ_OPTIONS_ERROR:       This implementation doesn't support the requested
+ *                          compression options. In the decoder this means
+ *                          that the header CRC32 matches, but the header
+ *                          itself specifies something that we don't support.
+ * @XZ_DATA_ERROR:          Compressed data is corrupt.
+ * @XZ_BUF_ERROR:           Cannot make any progress. Details are slightly
+ *                          different between multi-call and single-call
+ *                          mode; more information below.
+ *
+ * In multi-call mode, XZ_BUF_ERROR is returned when two consecutive calls
+ * to XZ code cannot consume any input and cannot produce any new output.
+ * This happens when there is no new input available, or the output buffer
+ * is full while at least one output byte is still pending. Assuming your
+ * code is not buggy, you can get this error only when decoding a compressed
+ * stream that is truncated or otherwise corrupt.
+ *
+ * In single-call mode, XZ_BUF_ERROR is returned only when the output buffer
+ * is too small or the compressed input is corrupt in a way that makes the
+ * decoder produce more output than the caller expected. When it is
+ * (relatively) clear that the compressed input is truncated, XZ_DATA_ERROR
+ * is used instead of XZ_BUF_ERROR.
+ */
+enum xz_ret {
+       XZ_OK,
+       XZ_STREAM_END,
+       XZ_UNSUPPORTED_CHECK,
+       XZ_MEM_ERROR,
+       XZ_MEMLIMIT_ERROR,
+       XZ_FORMAT_ERROR,
+       XZ_OPTIONS_ERROR,
+       XZ_DATA_ERROR,
+       XZ_BUF_ERROR
+};
+
+/**
+ * struct xz_buf - Passing input and output buffers to XZ code
+ * @in:         Beginning of the input buffer. This may be NULL if and only
+ *              if in_pos is equal to in_size.
+ * @in_pos:     Current position in the input buffer. This must not exceed
+ *              in_size.
+ * @in_size:    Size of the input buffer
+ * @out:        Beginning of the output buffer. This may be NULL if and only
+ *              if out_pos is equal to out_size.
+ * @out_pos:    Current position in the output buffer. This must not exceed
+ *              out_size.
+ * @out_size:   Size of the output buffer
+ *
+ * Only the contents of the output buffer from out[out_pos] onward, and
+ * the variables in_pos and out_pos are modified by the XZ code.
+ */
+struct xz_buf {
+       const uint8_t *in;
+       size_t in_pos;
+       size_t in_size;
+
+       uint8_t *out;
+       size_t out_pos;
+       size_t out_size;
+};
+
+/**
+ * struct xz_dec - Opaque type to hold the XZ decoder state
+ */
+struct xz_dec;
+
+/**
+ * xz_dec_init() - Allocate and initialize a XZ decoder state
+ * @mode:       Operation mode
+ * @dict_max:   Maximum size of the LZMA2 dictionary (history buffer) for
+ *              multi-call decoding. This is ignored in single-call mode
+ *              (mode == XZ_SINGLE). LZMA2 dictionary is always 2^n bytes
+ *              or 2^n + 2^(n-1) bytes (the latter sizes are less common
+ *              in practice), so other values for dict_max don't make sense.
+ *              In the kernel, dictionary sizes of 64 KiB, 128 KiB, 256 KiB,
+ *              512 KiB, and 1 MiB are probably the only reasonable values,
+ *              except for kernel and initramfs images where a bigger
+ *              dictionary can be fine and useful.
+ *
+ * Single-call mode (XZ_SINGLE): xz_dec_run() decodes the whole stream at
+ * once. The caller must provide enough output space or the decoding will
+ * fail. The output space is used as the dictionary buffer, which is why
+ * there is no need to allocate the dictionary as part of the decoder's
+ * internal state.
+ *
+ * Because the output buffer is used as the workspace, streams encoded using
+ * a big dictionary are not a problem in single-call mode. It is enough that
+ * the output buffer is big enough to hold the actual uncompressed data; it
+ * can be smaller than the dictionary size stored in the stream headers.
+ *
+ * Multi-call mode with preallocated dictionary (XZ_PREALLOC): dict_max bytes
+ * of memory is preallocated for the LZMA2 dictionary. This way there is no
+ * risk that xz_dec_run() could run out of memory, since xz_dec_run() will
+ * never allocate any memory. Instead, if the preallocated dictionary is too
+ * small for decoding the given input stream, xz_dec_run() will return
+ * XZ_MEMLIMIT_ERROR. Thus, it is important to know what kind of data will be
+ * decoded to avoid allocating excessive amount of memory for the dictionary.
+ *
+ * Multi-call mode with dynamically allocated dictionary (XZ_DYNALLOC):
+ * dict_max specifies the maximum allowed dictionary size that xz_dec_run()
+ * may allocate once it has parsed the dictionary size from the stream
+ * headers. This way excessive allocations can be avoided while still
+ * limiting the maximum memory usage to a sane value to prevent running the
+ * system out of memory when decompressing streams from untrusted sources.
+ *
+ * On success, xz_dec_init() returns a pointer to struct xz_dec, which is
+ * ready to be used with xz_dec_run(). If memory allocation fails,
+ * xz_dec_init() returns NULL.
+ */
+XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max);
+
+/**
+ * xz_dec_run() - Run the XZ decoder
+ * @s:          Decoder state allocated using xz_dec_init()
+ * @b:          Input and output buffers
+ *
+ * The possible return values depend on build options and operation mode.
+ * See enum xz_ret for details.
+ *
+ * Note that if an error occurs in single-call mode (return value is not
+ * XZ_STREAM_END), b->in_pos and b->out_pos are not modified and the
+ * contents of the output buffer from b->out[b->out_pos] onward are
+ * undefined. This is true even after XZ_BUF_ERROR, because with some filter
+ * chains, there may be a second pass over the output buffer, and this pass
+ * cannot be properly done if the output buffer is truncated. Thus, you
+ * cannot give the single-call decoder a too small buffer and then expect to
+ * get that amount valid data from the beginning of the stream. You must use
+ * the multi-call decoder if you don't want to uncompress the whole stream.
+ */
+XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b);
+
+/**
+ * xz_dec_reset() - Reset an already allocated decoder state
+ * @s:          Decoder state allocated using xz_dec_init()
+ *
+ * This function can be used to reset the multi-call decoder state without
+ * freeing and reallocating memory with xz_dec_end() and xz_dec_init().
+ *
+ * In single-call mode, xz_dec_reset() is always called in the beginning of
+ * xz_dec_run(). Thus, explicit call to xz_dec_reset() is useful only in
+ * multi-call mode.
+ */
+XZ_EXTERN void xz_dec_reset(struct xz_dec *s);
+
+/**
+ * xz_dec_end() - Free the memory allocated for the decoder state
+ * @s:          Decoder state allocated using xz_dec_init(). If s is NULL,
+ *              this function does nothing.
+ */
+XZ_EXTERN void xz_dec_end(struct xz_dec *s);
+
+/*
+ * Standalone build (userspace build or in-kernel build for boot time use)
+ * needs a CRC32 implementation. For normal in-kernel use, kernel's own
+ * CRC32 module is used instead, and users of this module don't need to
+ * care about the functions below.
+ */
+#ifndef XZ_INTERNAL_CRC32
+#      ifdef __KERNEL__
+#              define XZ_INTERNAL_CRC32 0
+#      else
+#              define XZ_INTERNAL_CRC32 1
+#      endif
+#endif
+
+/*
+ * If CRC64 support has been enabled with XZ_USE_CRC64, a CRC64
+ * implementation is needed too.
+ */
+#ifndef XZ_USE_CRC64
+#      undef XZ_INTERNAL_CRC64
+#      define XZ_INTERNAL_CRC64 0
+#endif
+#ifndef XZ_INTERNAL_CRC64
+#      ifdef __KERNEL__
+#              error Using CRC64 in the kernel has not been implemented.
+#      else
+#              define XZ_INTERNAL_CRC64 1
+#      endif
+#endif
+
+#if XZ_INTERNAL_CRC32
+/*
+ * This must be called before any other xz_* function to initialize
+ * the CRC32 lookup table.
+ */
+XZ_EXTERN void xz_crc32_init(void);
+
+/*
+ * Update CRC32 value using the polynomial from IEEE-802.3. To start a new
+ * calculation, the third argument must be zero. To continue the calculation,
+ * the previously returned value is passed as the third argument.
+ */
+XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc);
+#endif
+
+#if XZ_INTERNAL_CRC64
+/*
+ * This must be called before any other xz_* function (except xz_crc32_init())
+ * to initialize the CRC64 lookup table.
+ */
+XZ_EXTERN void xz_crc64_init(void);
+
+/*
+ * Update CRC64 value using the polynomial from ECMA-182. To start a new
+ * calculation, the third argument must be zero. To continue the calculation,
+ * the previously returned value is passed as the third argument.
+ */
+XZ_EXTERN uint64_t xz_crc64(const uint8_t *buf, size_t size, uint64_t crc);
+#endif
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/decompress_unxz.c b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/decompress_unxz.c
new file mode 100644 (file)
index 0000000..01aff84
--- /dev/null
@@ -0,0 +1,397 @@
+/*
+ * Wrapper for decompressing XZ-compressed kernel, initramfs, and initrd
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+/*
+ * Important notes about in-place decompression
+ *
+ * At least on x86, the kernel is decompressed in place: the compressed data
+ * is placed to the end of the output buffer, and the decompressor overwrites
+ * most of the compressed data. There must be enough safety margin to
+ * guarantee that the write position is always behind the read position.
+ *
+ * The safety margin for XZ with LZMA2 or BCJ+LZMA2 is calculated below.
+ * Note that the margin with XZ is bigger than with Deflate (gzip)!
+ *
+ * The worst case for in-place decompression is that the beginning of
+ * the file is compressed extremely well, and the rest of the file is
+ * uncompressible. Thus, we must look for worst-case expansion when the
+ * compressor is encoding uncompressible data.
+ *
+ * The structure of the .xz file in case of a compresed kernel is as follows.
+ * Sizes (as bytes) of the fields are in parenthesis.
+ *
+ *    Stream Header (12)
+ *    Block Header:
+ *      Block Header (8-12)
+ *      Compressed Data (N)
+ *      Block Padding (0-3)
+ *      CRC32 (4)
+ *    Index (8-20)
+ *    Stream Footer (12)
+ *
+ * Normally there is exactly one Block, but let's assume that there are
+ * 2-4 Blocks just in case. Because Stream Header and also Block Header
+ * of the first Block don't make the decompressor produce any uncompressed
+ * data, we can ignore them from our calculations. Block Headers of possible
+ * additional Blocks have to be taken into account still. With these
+ * assumptions, it is safe to assume that the total header overhead is
+ * less than 128 bytes.
+ *
+ * Compressed Data contains LZMA2 or BCJ+LZMA2 encoded data. Since BCJ
+ * doesn't change the size of the data, it is enough to calculate the
+ * safety margin for LZMA2.
+ *
+ * LZMA2 stores the data in chunks. Each chunk has a header whose size is
+ * a maximum of 6 bytes, but to get round 2^n numbers, let's assume that
+ * the maximum chunk header size is 8 bytes. After the chunk header, there
+ * may be up to 64 KiB of actual payload in the chunk. Often the payload is
+ * quite a bit smaller though; to be safe, let's assume that an average
+ * chunk has only 32 KiB of payload.
+ *
+ * The maximum uncompressed size of the payload is 2 MiB. The minimum
+ * uncompressed size of the payload is in practice never less than the
+ * payload size itself. The LZMA2 format would allow uncompressed size
+ * to be less than the payload size, but no sane compressor creates such
+ * files. LZMA2 supports storing uncompressible data in uncompressed form,
+ * so there's never a need to create payloads whose uncompressed size is
+ * smaller than the compressed size.
+ *
+ * The assumption, that the uncompressed size of the payload is never
+ * smaller than the payload itself, is valid only when talking about
+ * the payload as a whole. It is possible that the payload has parts where
+ * the decompressor consumes more input than it produces output. Calculating
+ * the worst case for this would be tricky. Instead of trying to do that,
+ * let's simply make sure that the decompressor never overwrites any bytes
+ * of the payload which it is currently reading.
+ *
+ * Now we have enough information to calculate the safety margin. We need
+ *   - 128 bytes for the .xz file format headers;
+ *   - 8 bytes per every 32 KiB of uncompressed size (one LZMA2 chunk header
+ *     per chunk, each chunk having average payload size of 32 KiB); and
+ *   - 64 KiB (biggest possible LZMA2 chunk payload size) to make sure that
+ *     the decompressor never overwrites anything from the LZMA2 chunk
+ *     payload it is currently reading.
+ *
+ * We get the following formula:
+ *
+ *    safety_margin = 128 + uncompressed_size * 8 / 32768 + 65536
+ *                  = 128 + (uncompressed_size >> 12) + 65536
+ *
+ * For comparison, according to arch/x86/boot/compressed/misc.c, the
+ * equivalent formula for Deflate is this:
+ *
+ *    safety_margin = 18 + (uncompressed_size >> 12) + 32768
+ *
+ * Thus, when updating Deflate-only in-place kernel decompressor to
+ * support XZ, the fixed overhead has to be increased from 18+32768 bytes
+ * to 128+65536 bytes.
+ */
+
+/*
+ * STATIC is defined to "static" if we are being built for kernel
+ * decompression (pre-boot code). <linux/decompress/mm.h> will define
+ * STATIC to empty if it wasn't already defined. Since we will need to
+ * know later if we are being used for kernel decompression, we define
+ * XZ_PREBOOT here.
+ */
+#ifdef STATIC
+#      define XZ_PREBOOT
+#endif
+#ifdef __KERNEL__
+#      include <linux/decompress/mm.h>
+#endif
+#define XZ_EXTERN STATIC
+
+#ifndef XZ_PREBOOT
+#      include <linux/slab.h>
+#      include <linux/xz.h>
+#else
+/*
+ * Use the internal CRC32 code instead of kernel's CRC32 module, which
+ * is not available in early phase of booting.
+ */
+#define XZ_INTERNAL_CRC32 1
+
+/*
+ * For boot time use, we enable only the BCJ filter of the current
+ * architecture or none if no BCJ filter is available for the architecture.
+ */
+#ifdef CONFIG_X86
+#      define XZ_DEC_X86
+#endif
+#ifdef CONFIG_PPC
+#      define XZ_DEC_POWERPC
+#endif
+#ifdef CONFIG_ARM
+#      define XZ_DEC_ARM
+#endif
+#ifdef CONFIG_IA64
+#      define XZ_DEC_IA64
+#endif
+#ifdef CONFIG_SPARC
+#      define XZ_DEC_SPARC
+#endif
+
+/*
+ * This will get the basic headers so that memeq() and others
+ * can be defined.
+ */
+#include "xz/xz_private.h"
+
+/*
+ * Replace the normal allocation functions with the versions from
+ * <linux/decompress/mm.h>. vfree() needs to support vfree(NULL)
+ * when XZ_DYNALLOC is used, but the pre-boot free() doesn't support it.
+ * Workaround it here because the other decompressors don't need it.
+ */
+#undef kmalloc
+#undef kfree
+#undef vmalloc
+#undef vfree
+#define kmalloc(size, flags) malloc(size)
+#define kfree(ptr) free(ptr)
+#define vmalloc(size) malloc(size)
+#define vfree(ptr) do { if (ptr != NULL) free(ptr); } while (0)
+
+/*
+ * FIXME: Not all basic memory functions are provided in architecture-specific
+ * files (yet). We define our own versions here for now, but this should be
+ * only a temporary solution.
+ *
+ * memeq and memzero are not used much and any remotely sane implementation
+ * is fast enough. memcpy/memmove speed matters in multi-call mode, but
+ * the kernel image is decompressed in single-call mode, in which only
+ * memcpy speed can matter and only if there is a lot of uncompressible data
+ * (LZMA2 stores uncompressible chunks in uncompressed form). Thus, the
+ * functions below should just be kept small; it's probably not worth
+ * optimizing for speed.
+ */
+
+#ifndef memeq
+static bool memeq(const void *a, const void *b, size_t size)
+{
+       const uint8_t *x = a;
+       const uint8_t *y = b;
+       size_t i;
+
+       for (i = 0; i < size; ++i)
+               if (x[i] != y[i])
+                       return false;
+
+       return true;
+}
+#endif
+
+#ifndef memzero
+static void memzero(void *buf, size_t size)
+{
+       uint8_t *b = buf;
+       uint8_t *e = b + size;
+
+       while (b != e)
+               *b++ = '\0';
+}
+#endif
+
+#if 0
+/* Not static to avoid a conflict with the prototype in the Linux headers. */
+void *memmove(void *dest, const void *src, size_t size)
+{
+       uint8_t *d = dest;
+       const uint8_t *s = src;
+       size_t i;
+
+       if (d < s) {
+               for (i = 0; i < size; ++i)
+                       d[i] = s[i];
+       } else if (d > s) {
+               i = size;
+               while (i-- > 0)
+                       d[i] = s[i];
+       }
+
+       return dest;
+}
+#endif
+
+/*
+ * Since we need memmove anyway, would use it as memcpy too.
+ * Commented out for now to avoid breaking things.
+ */
+/*
+#ifndef memcpy
+#      define memcpy memmove
+#endif
+*/
+
+#include "xz/xz_crc32.c"
+#include "xz/xz_dec_stream.c"
+#include "xz/xz_dec_lzma2.c"
+#include "xz/xz_dec_bcj.c"
+
+#endif /* XZ_PREBOOT */
+
+/* Size of the input and output buffers in multi-call mode */
+#define XZ_IOBUF_SIZE 4096
+
+/*
+ * This function implements the API defined in <linux/decompress/generic.h>.
+ *
+ * This wrapper will automatically choose single-call or multi-call mode
+ * of the native XZ decoder API. The single-call mode can be used only when
+ * both input and output buffers are available as a single chunk, i.e. when
+ * fill() and flush() won't be used.
+ */
+int INIT unxz(unsigned char *in, int in_size,
+                    int (*fill)(void *dest, unsigned int size),
+                    int (*flush)(void *src, unsigned int size),
+                    unsigned char *out, int *in_used,
+                    void (*error)(char *x))
+{
+       struct xz_buf b;
+       struct xz_dec *s;
+       enum xz_ret ret;
+       bool must_free_in = false;
+
+#if XZ_INTERNAL_CRC32
+       xz_crc32_init();
+#endif
+
+       if (in_used != NULL)
+               *in_used = 0;
+
+       if (fill == NULL && flush == NULL)
+               s = xz_dec_init(XZ_SINGLE, 0);
+       else
+               s = xz_dec_init(XZ_DYNALLOC, (uint32_t)-1);
+
+       if (s == NULL)
+               goto error_alloc_state;
+
+       if (flush == NULL) {
+               b.out = out;
+               b.out_size = (size_t)-1;
+       } else {
+               b.out_size = XZ_IOBUF_SIZE;
+               b.out = malloc(XZ_IOBUF_SIZE);
+               if (b.out == NULL)
+                       goto error_alloc_out;
+       }
+
+       if (in == NULL) {
+               must_free_in = true;
+               in = malloc(XZ_IOBUF_SIZE);
+               if (in == NULL)
+                       goto error_alloc_in;
+       }
+
+       b.in = in;
+       b.in_pos = 0;
+       b.in_size = in_size;
+       b.out_pos = 0;
+
+       if (fill == NULL && flush == NULL) {
+               ret = xz_dec_run(s, &b);
+       } else {
+               do {
+                       if (b.in_pos == b.in_size && fill != NULL) {
+                               if (in_used != NULL)
+                                       *in_used += b.in_pos;
+
+                               b.in_pos = 0;
+
+                               in_size = fill(in, XZ_IOBUF_SIZE);
+                               if (in_size < 0) {
+                                       /*
+                                        * This isn't an optimal error code
+                                        * but it probably isn't worth making
+                                        * a new one either.
+                                        */
+                                       ret = XZ_BUF_ERROR;
+                                       break;
+                               }
+
+                               b.in_size = in_size;
+                       }
+
+                       ret = xz_dec_run(s, &b);
+
+                       if (flush != NULL && (b.out_pos == b.out_size
+                                       || (ret != XZ_OK && b.out_pos > 0))) {
+                               /*
+                                * Setting ret here may hide an error
+                                * returned by xz_dec_run(), but probably
+                                * it's not too bad.
+                                */
+                               if (flush(b.out, b.out_pos) != (int)b.out_pos)
+                                       ret = XZ_BUF_ERROR;
+
+                               b.out_pos = 0;
+                       }
+               } while (ret == XZ_OK);
+
+               if (must_free_in)
+                       free(in);
+
+               if (flush != NULL)
+                       free(b.out);
+       }
+
+       if (in_used != NULL)
+               *in_used += b.in_pos;
+
+       xz_dec_end(s);
+
+       switch (ret) {
+       case XZ_STREAM_END:
+               return 0;
+
+       case XZ_MEM_ERROR:
+               /* This can occur only in multi-call mode. */
+               error("XZ decompressor ran out of memory");
+               break;
+
+       case XZ_FORMAT_ERROR:
+               error("Input is not in the XZ format (wrong magic bytes)");
+               break;
+
+       case XZ_OPTIONS_ERROR:
+               error("Input was encoded with settings that are not "
+                               "supported by this XZ decoder");
+               break;
+
+       case XZ_DATA_ERROR:
+       case XZ_BUF_ERROR:
+               error("XZ-compressed data is corrupt");
+               break;
+
+       default:
+               error("Bug in the XZ decompressor");
+               break;
+       }
+
+       return -1;
+
+error_alloc_in:
+       if (flush != NULL)
+               free(b.out);
+
+error_alloc_out:
+       xz_dec_end(s);
+
+error_alloc_state:
+       error("XZ decompressor ran out of memory");
+       return -1;
+}
+
+/*
+ * This macro is used by architecture-specific files to decompress
+ * the kernel image.
+ */
+#define decompress unxz
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/Kconfig b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/Kconfig
new file mode 100644 (file)
index 0000000..08837db
--- /dev/null
@@ -0,0 +1,57 @@
+config XZ_DEC
+       tristate "XZ decompression support"
+       select CRC32
+       help
+         LZMA2 compression algorithm and BCJ filters are supported using
+         the .xz file format as the container. For integrity checking,
+         CRC32 is supported. See Documentation/xz.txt for more information.
+
+if XZ_DEC
+
+config XZ_DEC_X86
+       bool "x86 BCJ filter decoder"
+       default y if X86
+       select XZ_DEC_BCJ
+
+config XZ_DEC_POWERPC
+       bool "PowerPC BCJ filter decoder"
+       default y if PPC
+       select XZ_DEC_BCJ
+
+config XZ_DEC_IA64
+       bool "IA-64 BCJ filter decoder"
+       default y if IA64
+       select XZ_DEC_BCJ
+
+config XZ_DEC_ARM
+       bool "ARM BCJ filter decoder"
+       default y if ARM
+       select XZ_DEC_BCJ
+
+config XZ_DEC_ARMTHUMB
+       bool "ARM-Thumb BCJ filter decoder"
+       default y if (ARM && ARM_THUMB)
+       select XZ_DEC_BCJ
+
+config XZ_DEC_SPARC
+       bool "SPARC BCJ filter decoder"
+       default y if SPARC
+       select XZ_DEC_BCJ
+
+endif
+
+config XZ_DEC_BCJ
+       bool
+       default n
+
+config XZ_DEC_TEST
+       tristate "XZ decompressor tester"
+       default n
+       depends on XZ_DEC
+       help
+         This allows passing .xz files to the in-kernel XZ decoder via
+         a character special file. It calculates CRC32 of the decompressed
+         data and writes diagnostics to the system log.
+
+         Unless you are developing the XZ decoder, you don't need this
+         and should say N.
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/Makefile b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/Makefile
new file mode 100644 (file)
index 0000000..a7fa769
--- /dev/null
@@ -0,0 +1,5 @@
+obj-$(CONFIG_XZ_DEC) += xz_dec.o
+xz_dec-y := xz_dec_syms.o xz_dec_stream.o xz_dec_lzma2.o
+xz_dec-$(CONFIG_XZ_DEC_BCJ) += xz_dec_bcj.o
+
+obj-$(CONFIG_XZ_DEC_TEST) += xz_dec_test.o
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_crc32.c b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_crc32.c
new file mode 100644 (file)
index 0000000..34532d1
--- /dev/null
@@ -0,0 +1,59 @@
+/*
+ * CRC32 using the polynomial from IEEE-802.3
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ *          Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+/*
+ * This is not the fastest implementation, but it is pretty compact.
+ * The fastest versions of xz_crc32() on modern CPUs without hardware
+ * accelerated CRC instruction are 3-5 times as fast as this version,
+ * but they are bigger and use more memory for the lookup table.
+ */
+
+#include "xz_private.h"
+
+/*
+ * STATIC_RW_DATA is used in the pre-boot environment on some architectures.
+ * See <linux/decompress/mm.h> for details.
+ */
+#ifndef STATIC_RW_DATA
+#      define STATIC_RW_DATA static
+#endif
+
+STATIC_RW_DATA uint32_t xz_crc32_table[256];
+
+XZ_EXTERN void xz_crc32_init(void)
+{
+       const uint32_t poly = 0xEDB88320;
+
+       uint32_t i;
+       uint32_t j;
+       uint32_t r;
+
+       for (i = 0; i < 256; ++i) {
+               r = i;
+               for (j = 0; j < 8; ++j)
+                       r = (r >> 1) ^ (poly & ~((r & 1) - 1));
+
+               xz_crc32_table[i] = r;
+       }
+
+       return;
+}
+
+XZ_EXTERN uint32_t xz_crc32(const uint8_t *buf, size_t size, uint32_t crc)
+{
+       crc = ~crc;
+
+       while (size != 0) {
+               crc = xz_crc32_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8);
+               --size;
+       }
+
+       return ~crc;
+}
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_crc64.c b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_crc64.c
new file mode 100644 (file)
index 0000000..ca1caee
--- /dev/null
@@ -0,0 +1,50 @@
+/*
+ * CRC64 using the polynomial from ECMA-182
+ *
+ * This file is similar to xz_crc32.c. See the comments there.
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ *          Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+
+#ifndef STATIC_RW_DATA
+#      define STATIC_RW_DATA static
+#endif
+
+STATIC_RW_DATA uint64_t xz_crc64_table[256];
+
+XZ_EXTERN void xz_crc64_init(void)
+{
+       const uint64_t poly = 0xC96C5795D7870F42;
+
+       uint32_t i;
+       uint32_t j;
+       uint64_t r;
+
+       for (i = 0; i < 256; ++i) {
+               r = i;
+               for (j = 0; j < 8; ++j)
+                       r = (r >> 1) ^ (poly & ~((r & 1) - 1));
+
+               xz_crc64_table[i] = r;
+       }
+
+       return;
+}
+
+XZ_EXTERN uint64_t xz_crc64(const uint8_t *buf, size_t size, uint64_t crc)
+{
+       crc = ~crc;
+
+       while (size != 0) {
+               crc = xz_crc64_table[*buf++ ^ (crc & 0xFF)] ^ (crc >> 8);
+               --size;
+       }
+
+       return ~crc;
+}
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_dec_bcj.c b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_dec_bcj.c
new file mode 100644 (file)
index 0000000..a768e6d
--- /dev/null
@@ -0,0 +1,574 @@
+/*
+ * Branch/Call/Jump (BCJ) filter decoders
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ *          Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+
+/*
+ * The rest of the file is inside this ifdef. It makes things a little more
+ * convenient when building without support for any BCJ filters.
+ */
+#ifdef XZ_DEC_BCJ
+
+struct xz_dec_bcj {
+       /* Type of the BCJ filter being used */
+       enum {
+               BCJ_X86 = 4,        /* x86 or x86-64 */
+               BCJ_POWERPC = 5,    /* Big endian only */
+               BCJ_IA64 = 6,       /* Big or little endian */
+               BCJ_ARM = 7,        /* Little endian only */
+               BCJ_ARMTHUMB = 8,   /* Little endian only */
+               BCJ_SPARC = 9       /* Big or little endian */
+       } type;
+
+       /*
+        * Return value of the next filter in the chain. We need to preserve
+        * this information across calls, because we must not call the next
+        * filter anymore once it has returned XZ_STREAM_END.
+        */
+       enum xz_ret ret;
+
+       /* True if we are operating in single-call mode. */
+       bool single_call;
+
+       /*
+        * Absolute position relative to the beginning of the uncompressed
+        * data (in a single .xz Block). We care only about the lowest 32
+        * bits so this doesn't need to be uint64_t even with big files.
+        */
+       uint32_t pos;
+
+       /* x86 filter state */
+       uint32_t x86_prev_mask;
+
+       /* Temporary space to hold the variables from struct xz_buf */
+       uint8_t *out;
+       size_t out_pos;
+       size_t out_size;
+
+       struct {
+               /* Amount of already filtered data in the beginning of buf */
+               size_t filtered;
+
+               /* Total amount of data currently stored in buf  */
+               size_t size;
+
+               /*
+                * Buffer to hold a mix of filtered and unfiltered data. This
+                * needs to be big enough to hold Alignment + 2 * Look-ahead:
+                *
+                * Type         Alignment   Look-ahead
+                * x86              1           4
+                * PowerPC          4           0
+                * IA-64           16           0
+                * ARM              4           0
+                * ARM-Thumb        2           2
+                * SPARC            4           0
+                */
+               uint8_t buf[16];
+       } temp;
+};
+
+#ifdef XZ_DEC_X86
+/*
+ * This is used to test the most significant byte of a memory address
+ * in an x86 instruction.
+ */
+static inline int bcj_x86_test_msbyte(uint8_t b)
+{
+       return b == 0x00 || b == 0xFF;
+}
+
+static size_t bcj_x86(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+       static const bool mask_to_allowed_status[8]
+               = { true, true, true, false, true, false, false, false };
+
+       static const uint8_t mask_to_bit_num[8] = { 0, 1, 2, 2, 3, 3, 3, 3 };
+
+       size_t i;
+       size_t prev_pos = (size_t)-1;
+       uint32_t prev_mask = s->x86_prev_mask;
+       uint32_t src;
+       uint32_t dest;
+       uint32_t j;
+       uint8_t b;
+
+       if (size <= 4)
+               return 0;
+
+       size -= 4;
+       for (i = 0; i < size; ++i) {
+               if ((buf[i] & 0xFE) != 0xE8)
+                       continue;
+
+               prev_pos = i - prev_pos;
+               if (prev_pos > 3) {
+                       prev_mask = 0;
+               } else {
+                       prev_mask = (prev_mask << (prev_pos - 1)) & 7;
+                       if (prev_mask != 0) {
+                               b = buf[i + 4 - mask_to_bit_num[prev_mask]];
+                               if (!mask_to_allowed_status[prev_mask]
+                                               || bcj_x86_test_msbyte(b)) {
+                                       prev_pos = i;
+                                       prev_mask = (prev_mask << 1) | 1;
+                                       continue;
+                               }
+                       }
+               }
+
+               prev_pos = i;
+
+               if (bcj_x86_test_msbyte(buf[i + 4])) {
+                       src = get_unaligned_le32(buf + i + 1);
+                       while (true) {
+                               dest = src - (s->pos + (uint32_t)i + 5);
+                               if (prev_mask == 0)
+                                       break;
+
+                               j = mask_to_bit_num[prev_mask] * 8;
+                               b = (uint8_t)(dest >> (24 - j));
+                               if (!bcj_x86_test_msbyte(b))
+                                       break;
+
+                               src = dest ^ (((uint32_t)1 << (32 - j)) - 1);
+                       }
+
+                       dest &= 0x01FFFFFF;
+                       dest |= (uint32_t)0 - (dest & 0x01000000);
+                       put_unaligned_le32(dest, buf + i + 1);
+                       i += 4;
+               } else {
+                       prev_mask = (prev_mask << 1) | 1;
+               }
+       }
+
+       prev_pos = i - prev_pos;
+       s->x86_prev_mask = prev_pos > 3 ? 0 : prev_mask << (prev_pos - 1);
+       return i;
+}
+#endif
+
+#ifdef XZ_DEC_POWERPC
+static size_t bcj_powerpc(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+       size_t i;
+       uint32_t instr;
+
+       for (i = 0; i + 4 <= size; i += 4) {
+               instr = get_unaligned_be32(buf + i);
+               if ((instr & 0xFC000003) == 0x48000001) {
+                       instr &= 0x03FFFFFC;
+                       instr -= s->pos + (uint32_t)i;
+                       instr &= 0x03FFFFFC;
+                       instr |= 0x48000001;
+                       put_unaligned_be32(instr, buf + i);
+               }
+       }
+
+       return i;
+}
+#endif
+
+#ifdef XZ_DEC_IA64
+static size_t bcj_ia64(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+       static const uint8_t branch_table[32] = {
+               0, 0, 0, 0, 0, 0, 0, 0,
+               0, 0, 0, 0, 0, 0, 0, 0,
+               4, 4, 6, 6, 0, 0, 7, 7,
+               4, 4, 0, 0, 4, 4, 0, 0
+       };
+
+       /*
+        * The local variables take a little bit stack space, but it's less
+        * than what LZMA2 decoder takes, so it doesn't make sense to reduce
+        * stack usage here without doing that for the LZMA2 decoder too.
+        */
+
+       /* Loop counters */
+       size_t i;
+       size_t j;
+
+       /* Instruction slot (0, 1, or 2) in the 128-bit instruction word */
+       uint32_t slot;
+
+       /* Bitwise offset of the instruction indicated by slot */
+       uint32_t bit_pos;
+
+       /* bit_pos split into byte and bit parts */
+       uint32_t byte_pos;
+       uint32_t bit_res;
+
+       /* Address part of an instruction */
+       uint32_t addr;
+
+       /* Mask used to detect which instructions to convert */
+       uint32_t mask;
+
+       /* 41-bit instruction stored somewhere in the lowest 48 bits */
+       uint64_t instr;
+
+       /* Instruction normalized with bit_res for easier manipulation */
+       uint64_t norm;
+
+       for (i = 0; i + 16 <= size; i += 16) {
+               mask = branch_table[buf[i] & 0x1F];
+               for (slot = 0, bit_pos = 5; slot < 3; ++slot, bit_pos += 41) {
+                       if (((mask >> slot) & 1) == 0)
+                               continue;
+
+                       byte_pos = bit_pos >> 3;
+                       bit_res = bit_pos & 7;
+                       instr = 0;
+                       for (j = 0; j < 6; ++j)
+                               instr |= (uint64_t)(buf[i + j + byte_pos])
+                                               << (8 * j);
+
+                       norm = instr >> bit_res;
+
+                       if (((norm >> 37) & 0x0F) == 0x05
+                                       && ((norm >> 9) & 0x07) == 0) {
+                               addr = (norm >> 13) & 0x0FFFFF;
+                               addr |= ((uint32_t)(norm >> 36) & 1) << 20;
+                               addr <<= 4;
+                               addr -= s->pos + (uint32_t)i;
+                               addr >>= 4;
+
+                               norm &= ~((uint64_t)0x8FFFFF << 13);
+                               norm |= (uint64_t)(addr & 0x0FFFFF) << 13;
+                               norm |= (uint64_t)(addr & 0x100000)
+                                               << (36 - 20);
+
+                               instr &= (1 << bit_res) - 1;
+                               instr |= norm << bit_res;
+
+                               for (j = 0; j < 6; j++)
+                                       buf[i + j + byte_pos]
+                                               = (uint8_t)(instr >> (8 * j));
+                       }
+               }
+       }
+
+       return i;
+}
+#endif
+
+#ifdef XZ_DEC_ARM
+static size_t bcj_arm(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+       size_t i;
+       uint32_t addr;
+
+       for (i = 0; i + 4 <= size; i += 4) {
+               if (buf[i + 3] == 0xEB) {
+                       addr = (uint32_t)buf[i] | ((uint32_t)buf[i + 1] << 8)
+                                       | ((uint32_t)buf[i + 2] << 16);
+                       addr <<= 2;
+                       addr -= s->pos + (uint32_t)i + 8;
+                       addr >>= 2;
+                       buf[i] = (uint8_t)addr;
+                       buf[i + 1] = (uint8_t)(addr >> 8);
+                       buf[i + 2] = (uint8_t)(addr >> 16);
+               }
+       }
+
+       return i;
+}
+#endif
+
+#ifdef XZ_DEC_ARMTHUMB
+static size_t bcj_armthumb(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+       size_t i;
+       uint32_t addr;
+
+       for (i = 0; i + 4 <= size; i += 2) {
+               if ((buf[i + 1] & 0xF8) == 0xF0
+                               && (buf[i + 3] & 0xF8) == 0xF8) {
+                       addr = (((uint32_t)buf[i + 1] & 0x07) << 19)
+                                       | ((uint32_t)buf[i] << 11)
+                                       | (((uint32_t)buf[i + 3] & 0x07) << 8)
+                                       | (uint32_t)buf[i + 2];
+                       addr <<= 1;
+                       addr -= s->pos + (uint32_t)i + 4;
+                       addr >>= 1;
+                       buf[i + 1] = (uint8_t)(0xF0 | ((addr >> 19) & 0x07));
+                       buf[i] = (uint8_t)(addr >> 11);
+                       buf[i + 3] = (uint8_t)(0xF8 | ((addr >> 8) & 0x07));
+                       buf[i + 2] = (uint8_t)addr;
+                       i += 2;
+               }
+       }
+
+       return i;
+}
+#endif
+
+#ifdef XZ_DEC_SPARC
+static size_t bcj_sparc(struct xz_dec_bcj *s, uint8_t *buf, size_t size)
+{
+       size_t i;
+       uint32_t instr;
+
+       for (i = 0; i + 4 <= size; i += 4) {
+               instr = get_unaligned_be32(buf + i);
+               if ((instr >> 22) == 0x100 || (instr >> 22) == 0x1FF) {
+                       instr <<= 2;
+                       instr -= s->pos + (uint32_t)i;
+                       instr >>= 2;
+                       instr = ((uint32_t)0x40000000 - (instr & 0x400000))
+                                       | 0x40000000 | (instr & 0x3FFFFF);
+                       put_unaligned_be32(instr, buf + i);
+               }
+       }
+
+       return i;
+}
+#endif
+
+/*
+ * Apply the selected BCJ filter. Update *pos and s->pos to match the amount
+ * of data that got filtered.
+ *
+ * NOTE: This is implemented as a switch statement to avoid using function
+ * pointers, which could be problematic in the kernel boot code, which must
+ * avoid pointers to static data (at least on x86).
+ */
+static void bcj_apply(struct xz_dec_bcj *s,
+                     uint8_t *buf, size_t *pos, size_t size)
+{
+       size_t filtered;
+
+       buf += *pos;
+       size -= *pos;
+
+       switch (s->type) {
+#ifdef XZ_DEC_X86
+       case BCJ_X86:
+               filtered = bcj_x86(s, buf, size);
+               break;
+#endif
+#ifdef XZ_DEC_POWERPC
+       case BCJ_POWERPC:
+               filtered = bcj_powerpc(s, buf, size);
+               break;
+#endif
+#ifdef XZ_DEC_IA64
+       case BCJ_IA64:
+               filtered = bcj_ia64(s, buf, size);
+               break;
+#endif
+#ifdef XZ_DEC_ARM
+       case BCJ_ARM:
+               filtered = bcj_arm(s, buf, size);
+               break;
+#endif
+#ifdef XZ_DEC_ARMTHUMB
+       case BCJ_ARMTHUMB:
+               filtered = bcj_armthumb(s, buf, size);
+               break;
+#endif
+#ifdef XZ_DEC_SPARC
+       case BCJ_SPARC:
+               filtered = bcj_sparc(s, buf, size);
+               break;
+#endif
+       default:
+               /* Never reached but silence compiler warnings. */
+               filtered = 0;
+               break;
+       }
+
+       *pos += filtered;
+       s->pos += filtered;
+}
+
+/*
+ * Flush pending filtered data from temp to the output buffer.
+ * Move the remaining mixture of possibly filtered and unfiltered
+ * data to the beginning of temp.
+ */
+static void bcj_flush(struct xz_dec_bcj *s, struct xz_buf *b)
+{
+       size_t copy_size;
+
+       copy_size = min_t(size_t, s->temp.filtered, b->out_size - b->out_pos);
+       memcpy(b->out + b->out_pos, s->temp.buf, copy_size);
+       b->out_pos += copy_size;
+
+       s->temp.filtered -= copy_size;
+       s->temp.size -= copy_size;
+       memmove(s->temp.buf, s->temp.buf + copy_size, s->temp.size);
+}
+
+/*
+ * The BCJ filter functions are primitive in sense that they process the
+ * data in chunks of 1-16 bytes. To hide this issue, this function does
+ * some buffering.
+ */
+XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s,
+                                    struct xz_dec_lzma2 *lzma2,
+                                    struct xz_buf *b)
+{
+       size_t out_start;
+
+       /*
+        * Flush pending already filtered data to the output buffer. Return
+        * immediatelly if we couldn't flush everything, or if the next
+        * filter in the chain had already returned XZ_STREAM_END.
+        */
+       if (s->temp.filtered > 0) {
+               bcj_flush(s, b);
+               if (s->temp.filtered > 0)
+                       return XZ_OK;
+
+               if (s->ret == XZ_STREAM_END)
+                       return XZ_STREAM_END;
+       }
+
+       /*
+        * If we have more output space than what is currently pending in
+        * temp, copy the unfiltered data from temp to the output buffer
+        * and try to fill the output buffer by decoding more data from the
+        * next filter in the chain. Apply the BCJ filter on the new data
+        * in the output buffer. If everything cannot be filtered, copy it
+        * to temp and rewind the output buffer position accordingly.
+        *
+        * This needs to be always run when temp.size == 0 to handle a special
+        * case where the output buffer is full and the next filter has no
+        * more output coming but hasn't returned XZ_STREAM_END yet.
+        */
+       if (s->temp.size < b->out_size - b->out_pos || s->temp.size == 0) {
+               out_start = b->out_pos;
+               memcpy(b->out + b->out_pos, s->temp.buf, s->temp.size);
+               b->out_pos += s->temp.size;
+
+               s->ret = xz_dec_lzma2_run(lzma2, b);
+               if (s->ret != XZ_STREAM_END
+                               && (s->ret != XZ_OK || s->single_call))
+                       return s->ret;
+
+               bcj_apply(s, b->out, &out_start, b->out_pos);
+
+               /*
+                * As an exception, if the next filter returned XZ_STREAM_END,
+                * we can do that too, since the last few bytes that remain
+                * unfiltered are meant to remain unfiltered.
+                */
+               if (s->ret == XZ_STREAM_END)
+                       return XZ_STREAM_END;
+
+               s->temp.size = b->out_pos - out_start;
+               b->out_pos -= s->temp.size;
+               memcpy(s->temp.buf, b->out + b->out_pos, s->temp.size);
+
+               /*
+                * If there wasn't enough input to the next filter to fill
+                * the output buffer with unfiltered data, there's no point
+                * to try decoding more data to temp.
+                */
+               if (b->out_pos + s->temp.size < b->out_size)
+                       return XZ_OK;
+       }
+
+       /*
+        * We have unfiltered data in temp. If the output buffer isn't full
+        * yet, try to fill the temp buffer by decoding more data from the
+        * next filter. Apply the BCJ filter on temp. Then we hopefully can
+        * fill the actual output buffer by copying filtered data from temp.
+        * A mix of filtered and unfiltered data may be left in temp; it will
+        * be taken care on the next call to this function.
+        */
+       if (b->out_pos < b->out_size) {
+               /* Make b->out{,_pos,_size} temporarily point to s->temp. */
+               s->out = b->out;
+               s->out_pos = b->out_pos;
+               s->out_size = b->out_size;
+               b->out = s->temp.buf;
+               b->out_pos = s->temp.size;
+               b->out_size = sizeof(s->temp.buf);
+
+               s->ret = xz_dec_lzma2_run(lzma2, b);
+
+               s->temp.size = b->out_pos;
+               b->out = s->out;
+               b->out_pos = s->out_pos;
+               b->out_size = s->out_size;
+
+               if (s->ret != XZ_OK && s->ret != XZ_STREAM_END)
+                       return s->ret;
+
+               bcj_apply(s, s->temp.buf, &s->temp.filtered, s->temp.size);
+
+               /*
+                * If the next filter returned XZ_STREAM_END, we mark that
+                * everything is filtered, since the last unfiltered bytes
+                * of the stream are meant to be left as is.
+                */
+               if (s->ret == XZ_STREAM_END)
+                       s->temp.filtered = s->temp.size;
+
+               bcj_flush(s, b);
+               if (s->temp.filtered > 0)
+                       return XZ_OK;
+       }
+
+       return s->ret;
+}
+
+XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call)
+{
+       struct xz_dec_bcj *s = kmalloc(sizeof(*s), GFP_KERNEL);
+       if (s != NULL)
+               s->single_call = single_call;
+
+       return s;
+}
+
+XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id)
+{
+       switch (id) {
+#ifdef XZ_DEC_X86
+       case BCJ_X86:
+#endif
+#ifdef XZ_DEC_POWERPC
+       case BCJ_POWERPC:
+#endif
+#ifdef XZ_DEC_IA64
+       case BCJ_IA64:
+#endif
+#ifdef XZ_DEC_ARM
+       case BCJ_ARM:
+#endif
+#ifdef XZ_DEC_ARMTHUMB
+       case BCJ_ARMTHUMB:
+#endif
+#ifdef XZ_DEC_SPARC
+       case BCJ_SPARC:
+#endif
+               break;
+
+       default:
+               /* Unsupported Filter ID */
+               return XZ_OPTIONS_ERROR;
+       }
+
+       s->type = id;
+       s->ret = XZ_OK;
+       s->pos = 0;
+       s->x86_prev_mask = 0;
+       s->temp.filtered = 0;
+       s->temp.size = 0;
+
+       return XZ_OK;
+}
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_dec_lzma2.c b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_dec_lzma2.c
new file mode 100644 (file)
index 0000000..a6cdc96
--- /dev/null
@@ -0,0 +1,1171 @@
+/*
+ * LZMA2 decoder
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ *          Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+#include "xz_lzma2.h"
+
+/*
+ * Range decoder initialization eats the first five bytes of each LZMA chunk.
+ */
+#define RC_INIT_BYTES 5
+
+/*
+ * Minimum number of usable input buffer to safely decode one LZMA symbol.
+ * The worst case is that we decode 22 bits using probabilities and 26
+ * direct bits. This may decode at maximum of 20 bytes of input. However,
+ * lzma_main() does an extra normalization before returning, thus we
+ * need to put 21 here.
+ */
+#define LZMA_IN_REQUIRED 21
+
+/*
+ * Dictionary (history buffer)
+ *
+ * These are always true:
+ *    start <= pos <= full <= end
+ *    pos <= limit <= end
+ *
+ * In multi-call mode, also these are true:
+ *    end == size
+ *    size <= size_max
+ *    allocated <= size
+ *
+ * Most of these variables are size_t to support single-call mode,
+ * in which the dictionary variables address the actual output
+ * buffer directly.
+ */
+struct dictionary {
+       /* Beginning of the history buffer */
+       uint8_t *buf;
+
+       /* Old position in buf (before decoding more data) */
+       size_t start;
+
+       /* Position in buf */
+       size_t pos;
+
+       /*
+        * How full dictionary is. This is used to detect corrupt input that
+        * would read beyond the beginning of the uncompressed stream.
+        */
+       size_t full;
+
+       /* Write limit; we don't write to buf[limit] or later bytes. */
+       size_t limit;
+
+       /*
+        * End of the dictionary buffer. In multi-call mode, this is
+        * the same as the dictionary size. In single-call mode, this
+        * indicates the size of the output buffer.
+        */
+       size_t end;
+
+       /*
+        * Size of the dictionary as specified in Block Header. This is used
+        * together with "full" to detect corrupt input that would make us
+        * read beyond the beginning of the uncompressed stream.
+        */
+       uint32_t size;
+
+       /*
+        * Maximum allowed dictionary size in multi-call mode.
+        * This is ignored in single-call mode.
+        */
+       uint32_t size_max;
+
+       /*
+        * Amount of memory currently allocated for the dictionary.
+        * This is used only with XZ_DYNALLOC. (With XZ_PREALLOC,
+        * size_max is always the same as the allocated size.)
+        */
+       uint32_t allocated;
+
+       /* Operation mode */
+       enum xz_mode mode;
+};
+
+/* Range decoder */
+struct rc_dec {
+       uint32_t range;
+       uint32_t code;
+
+       /*
+        * Number of initializing bytes remaining to be read
+        * by rc_read_init().
+        */
+       uint32_t init_bytes_left;
+
+       /*
+        * Buffer from which we read our input. It can be either
+        * temp.buf or the caller-provided input buffer.
+        */
+       const uint8_t *in;
+       size_t in_pos;
+       size_t in_limit;
+};
+
+/* Probabilities for a length decoder. */
+struct lzma_len_dec {
+       /* Probability of match length being at least 10 */
+       uint16_t choice;
+
+       /* Probability of match length being at least 18 */
+       uint16_t choice2;
+
+       /* Probabilities for match lengths 2-9 */
+       uint16_t low[POS_STATES_MAX][LEN_LOW_SYMBOLS];
+
+       /* Probabilities for match lengths 10-17 */
+       uint16_t mid[POS_STATES_MAX][LEN_MID_SYMBOLS];
+
+       /* Probabilities for match lengths 18-273 */
+       uint16_t high[LEN_HIGH_SYMBOLS];
+};
+
+struct lzma_dec {
+       /* Distances of latest four matches */
+       uint32_t rep0;
+       uint32_t rep1;
+       uint32_t rep2;
+       uint32_t rep3;
+
+       /* Types of the most recently seen LZMA symbols */
+       enum lzma_state state;
+
+       /*
+        * Length of a match. This is updated so that dict_repeat can
+        * be called again to finish repeating the whole match.
+        */
+       uint32_t len;
+
+       /*
+        * LZMA properties or related bit masks (number of literal
+        * context bits, a mask dervied from the number of literal
+        * position bits, and a mask dervied from the number
+        * position bits)
+        */
+       uint32_t lc;
+       uint32_t literal_pos_mask; /* (1 << lp) - 1 */
+       uint32_t pos_mask;         /* (1 << pb) - 1 */
+
+       /* If 1, it's a match. Otherwise it's a single 8-bit literal. */
+       uint16_t is_match[STATES][POS_STATES_MAX];
+
+       /* If 1, it's a repeated match. The distance is one of rep0 .. rep3. */
+       uint16_t is_rep[STATES];
+
+       /*
+        * If 0, distance of a repeated match is rep0.
+        * Otherwise check is_rep1.
+        */
+       uint16_t is_rep0[STATES];
+
+       /*
+        * If 0, distance of a repeated match is rep1.
+        * Otherwise check is_rep2.
+        */
+       uint16_t is_rep1[STATES];
+
+       /* If 0, distance of a repeated match is rep2. Otherwise it is rep3. */
+       uint16_t is_rep2[STATES];
+
+       /*
+        * If 1, the repeated match has length of one byte. Otherwise
+        * the length is decoded from rep_len_decoder.
+        */
+       uint16_t is_rep0_long[STATES][POS_STATES_MAX];
+
+       /*
+        * Probability tree for the highest two bits of the match
+        * distance. There is a separate probability tree for match
+        * lengths of 2 (i.e. MATCH_LEN_MIN), 3, 4, and [5, 273].
+        */
+       uint16_t dist_slot[DIST_STATES][DIST_SLOTS];
+
+       /*
+        * Probility trees for additional bits for match distance
+        * when the distance is in the range [4, 127].
+        */
+       uint16_t dist_special[FULL_DISTANCES - DIST_MODEL_END];
+
+       /*
+        * Probability tree for the lowest four bits of a match
+        * distance that is equal to or greater than 128.
+        */
+       uint16_t dist_align[ALIGN_SIZE];
+
+       /* Length of a normal match */
+       struct lzma_len_dec match_len_dec;
+
+       /* Length of a repeated match */
+       struct lzma_len_dec rep_len_dec;
+
+       /* Probabilities of literals */
+       uint16_t literal[LITERAL_CODERS_MAX][LITERAL_CODER_SIZE];
+};
+
+struct lzma2_dec {
+       /* Position in xz_dec_lzma2_run(). */
+       enum lzma2_seq {
+               SEQ_CONTROL,
+               SEQ_UNCOMPRESSED_1,
+               SEQ_UNCOMPRESSED_2,
+               SEQ_COMPRESSED_0,
+               SEQ_COMPRESSED_1,
+               SEQ_PROPERTIES,
+               SEQ_LZMA_PREPARE,
+               SEQ_LZMA_RUN,
+               SEQ_COPY
+       } sequence;
+
+       /* Next position after decoding the compressed size of the chunk. */
+       enum lzma2_seq next_sequence;
+
+       /* Uncompressed size of LZMA chunk (2 MiB at maximum) */
+       uint32_t uncompressed;
+
+       /*
+        * Compressed size of LZMA chunk or compressed/uncompressed
+        * size of uncompressed chunk (64 KiB at maximum)
+        */
+       uint32_t compressed;
+
+       /*
+        * True if dictionary reset is needed. This is false before
+        * the first chunk (LZMA or uncompressed).
+        */
+       bool need_dict_reset;
+
+       /*
+        * True if new LZMA properties are needed. This is false
+        * before the first LZMA chunk.
+        */
+       bool need_props;
+};
+
+struct xz_dec_lzma2 {
+       /*
+        * The order below is important on x86 to reduce code size and
+        * it shouldn't hurt on other platforms. Everything up to and
+        * including lzma.pos_mask are in the first 128 bytes on x86-32,
+        * which allows using smaller instructions to access those
+        * variables. On x86-64, fewer variables fit into the first 128
+        * bytes, but this is still the best order without sacrificing
+        * the readability by splitting the structures.
+        */
+       struct rc_dec rc;
+       struct dictionary dict;
+       struct lzma2_dec lzma2;
+       struct lzma_dec lzma;
+
+       /*
+        * Temporary buffer which holds small number of input bytes between
+        * decoder calls. See lzma2_lzma() for details.
+        */
+       struct {
+               uint32_t size;
+               uint8_t buf[3 * LZMA_IN_REQUIRED];
+       } temp;
+};
+
+/**************
+ * Dictionary *
+ **************/
+
+/*
+ * Reset the dictionary state. When in single-call mode, set up the beginning
+ * of the dictionary to point to the actual output buffer.
+ */
+static void dict_reset(struct dictionary *dict, struct xz_buf *b)
+{
+       if (DEC_IS_SINGLE(dict->mode)) {
+               dict->buf = b->out + b->out_pos;
+               dict->end = b->out_size - b->out_pos;
+       }
+
+       dict->start = 0;
+       dict->pos = 0;
+       dict->limit = 0;
+       dict->full = 0;
+}
+
+/* Set dictionary write limit */
+static void dict_limit(struct dictionary *dict, size_t out_max)
+{
+       if (dict->end - dict->pos <= out_max)
+               dict->limit = dict->end;
+       else
+               dict->limit = dict->pos + out_max;
+}
+
+/* Return true if at least one byte can be written into the dictionary. */
+static inline bool dict_has_space(const struct dictionary *dict)
+{
+       return dict->pos < dict->limit;
+}
+
+/*
+ * Get a byte from the dictionary at the given distance. The distance is
+ * assumed to valid, or as a special case, zero when the dictionary is
+ * still empty. This special case is needed for single-call decoding to
+ * avoid writing a '\0' to the end of the destination buffer.
+ */
+static inline uint32_t dict_get(const struct dictionary *dict, uint32_t dist)
+{
+       size_t offset = dict->pos - dist - 1;
+
+       if (dist >= dict->pos)
+               offset += dict->end;
+
+       return dict->full > 0 ? dict->buf[offset] : 0;
+}
+
+/*
+ * Put one byte into the dictionary. It is assumed that there is space for it.
+ */
+static inline void dict_put(struct dictionary *dict, uint8_t byte)
+{
+       dict->buf[dict->pos++] = byte;
+
+       if (dict->full < dict->pos)
+               dict->full = dict->pos;
+}
+
+/*
+ * Repeat given number of bytes from the given distance. If the distance is
+ * invalid, false is returned. On success, true is returned and *len is
+ * updated to indicate how many bytes were left to be repeated.
+ */
+static bool dict_repeat(struct dictionary *dict, uint32_t *len, uint32_t dist)
+{
+       size_t back;
+       uint32_t left;
+
+       if (dist >= dict->full || dist >= dict->size)
+               return false;
+
+       left = min_t(size_t, dict->limit - dict->pos, *len);
+       *len -= left;
+
+       back = dict->pos - dist - 1;
+       if (dist >= dict->pos)
+               back += dict->end;
+
+       do {
+               dict->buf[dict->pos++] = dict->buf[back++];
+               if (back == dict->end)
+                       back = 0;
+       } while (--left > 0);
+
+       if (dict->full < dict->pos)
+               dict->full = dict->pos;
+
+       return true;
+}
+
+/* Copy uncompressed data as is from input to dictionary and output buffers. */
+static void dict_uncompressed(struct dictionary *dict, struct xz_buf *b,
+                             uint32_t *left)
+{
+       size_t copy_size;
+
+       while (*left > 0 && b->in_pos < b->in_size
+                       && b->out_pos < b->out_size) {
+               copy_size = min(b->in_size - b->in_pos,
+                               b->out_size - b->out_pos);
+               if (copy_size > dict->end - dict->pos)
+                       copy_size = dict->end - dict->pos;
+               if (copy_size > *left)
+                       copy_size = *left;
+
+               *left -= copy_size;
+
+               memcpy(dict->buf + dict->pos, b->in + b->in_pos, copy_size);
+               dict->pos += copy_size;
+
+               if (dict->full < dict->pos)
+                       dict->full = dict->pos;
+
+               if (DEC_IS_MULTI(dict->mode)) {
+                       if (dict->pos == dict->end)
+                               dict->pos = 0;
+
+                       memcpy(b->out + b->out_pos, b->in + b->in_pos,
+                                       copy_size);
+               }
+
+               dict->start = dict->pos;
+
+               b->out_pos += copy_size;
+               b->in_pos += copy_size;
+       }
+}
+
+/*
+ * Flush pending data from dictionary to b->out. It is assumed that there is
+ * enough space in b->out. This is guaranteed because caller uses dict_limit()
+ * before decoding data into the dictionary.
+ */
+static uint32_t dict_flush(struct dictionary *dict, struct xz_buf *b)
+{
+       size_t copy_size = dict->pos - dict->start;
+
+       if (DEC_IS_MULTI(dict->mode)) {
+               if (dict->pos == dict->end)
+                       dict->pos = 0;
+
+               memcpy(b->out + b->out_pos, dict->buf + dict->start,
+                               copy_size);
+       }
+
+       dict->start = dict->pos;
+       b->out_pos += copy_size;
+       return copy_size;
+}
+
+/*****************
+ * Range decoder *
+ *****************/
+
+/* Reset the range decoder. */
+static void rc_reset(struct rc_dec *rc)
+{
+       rc->range = (uint32_t)-1;
+       rc->code = 0;
+       rc->init_bytes_left = RC_INIT_BYTES;
+}
+
+/*
+ * Read the first five initial bytes into rc->code if they haven't been
+ * read already. (Yes, the first byte gets completely ignored.)
+ */
+static bool rc_read_init(struct rc_dec *rc, struct xz_buf *b)
+{
+       while (rc->init_bytes_left > 0) {
+               if (b->in_pos == b->in_size)
+                       return false;
+
+               rc->code = (rc->code << 8) + b->in[b->in_pos++];
+               --rc->init_bytes_left;
+       }
+
+       return true;
+}
+
+/* Return true if there may not be enough input for the next decoding loop. */
+static inline bool rc_limit_exceeded(const struct rc_dec *rc)
+{
+       return rc->in_pos > rc->in_limit;
+}
+
+/*
+ * Return true if it is possible (from point of view of range decoder) that
+ * we have reached the end of the LZMA chunk.
+ */
+static inline bool rc_is_finished(const struct rc_dec *rc)
+{
+       return rc->code == 0;
+}
+
+/* Read the next input byte if needed. */
+static __always_inline void rc_normalize(struct rc_dec *rc)
+{
+       if (rc->range < RC_TOP_VALUE) {
+               rc->range <<= RC_SHIFT_BITS;
+               rc->code = (rc->code << RC_SHIFT_BITS) + rc->in[rc->in_pos++];
+       }
+}
+
+/*
+ * Decode one bit. In some versions, this function has been splitted in three
+ * functions so that the compiler is supposed to be able to more easily avoid
+ * an extra branch. In this particular version of the LZMA decoder, this
+ * doesn't seem to be a good idea (tested with GCC 3.3.6, 3.4.6, and 4.3.3
+ * on x86). Using a non-splitted version results in nicer looking code too.
+ *
+ * NOTE: This must return an int. Do not make it return a bool or the speed
+ * of the code generated by GCC 3.x decreases 10-15 %. (GCC 4.3 doesn't care,
+ * and it generates 10-20 % faster code than GCC 3.x from this file anyway.)
+ */
+static __always_inline int rc_bit(struct rc_dec *rc, uint16_t *prob)
+{
+       uint32_t bound;
+       int bit;
+
+       rc_normalize(rc);
+       bound = (rc->range >> RC_BIT_MODEL_TOTAL_BITS) * *prob;
+       if (rc->code < bound) {
+               rc->range = bound;
+               *prob += (RC_BIT_MODEL_TOTAL - *prob) >> RC_MOVE_BITS;
+               bit = 0;
+       } else {
+               rc->range -= bound;
+               rc->code -= bound;
+               *prob -= *prob >> RC_MOVE_BITS;
+               bit = 1;
+       }
+
+       return bit;
+}
+
+/* Decode a bittree starting from the most significant bit. */
+static __always_inline uint32_t rc_bittree(struct rc_dec *rc,
+                                          uint16_t *probs, uint32_t limit)
+{
+       uint32_t symbol = 1;
+
+       do {
+               if (rc_bit(rc, &probs[symbol]))
+                       symbol = (symbol << 1) + 1;
+               else
+                       symbol <<= 1;
+       } while (symbol < limit);
+
+       return symbol;
+}
+
+/* Decode a bittree starting from the least significant bit. */
+static __always_inline void rc_bittree_reverse(struct rc_dec *rc,
+                                              uint16_t *probs,
+                                              uint32_t *dest, uint32_t limit)
+{
+       uint32_t symbol = 1;
+       uint32_t i = 0;
+
+       do {
+               if (rc_bit(rc, &probs[symbol])) {
+                       symbol = (symbol << 1) + 1;
+                       *dest += 1 << i;
+               } else {
+                       symbol <<= 1;
+               }
+       } while (++i < limit);
+}
+
+/* Decode direct bits (fixed fifty-fifty probability) */
+static inline void rc_direct(struct rc_dec *rc, uint32_t *dest, uint32_t limit)
+{
+       uint32_t mask;
+
+       do {
+               rc_normalize(rc);
+               rc->range >>= 1;
+               rc->code -= rc->range;
+               mask = (uint32_t)0 - (rc->code >> 31);
+               rc->code += rc->range & mask;
+               *dest = (*dest << 1) + (mask + 1);
+       } while (--limit > 0);
+}
+
+/********
+ * LZMA *
+ ********/
+
+/* Get pointer to literal coder probability array. */
+static uint16_t *lzma_literal_probs(struct xz_dec_lzma2 *s)
+{
+       uint32_t prev_byte = dict_get(&s->dict, 0);
+       uint32_t low = prev_byte >> (8 - s->lzma.lc);
+       uint32_t high = (s->dict.pos & s->lzma.literal_pos_mask) << s->lzma.lc;
+       return s->lzma.literal[low + high];
+}
+
+/* Decode a literal (one 8-bit byte) */
+static void lzma_literal(struct xz_dec_lzma2 *s)
+{
+       uint16_t *probs;
+       uint32_t symbol;
+       uint32_t match_byte;
+       uint32_t match_bit;
+       uint32_t offset;
+       uint32_t i;
+
+       probs = lzma_literal_probs(s);
+
+       if (lzma_state_is_literal(s->lzma.state)) {
+               symbol = rc_bittree(&s->rc, probs, 0x100);
+       } else {
+               symbol = 1;
+               match_byte = dict_get(&s->dict, s->lzma.rep0) << 1;
+               offset = 0x100;
+
+               do {
+                       match_bit = match_byte & offset;
+                       match_byte <<= 1;
+                       i = offset + match_bit + symbol;
+
+                       if (rc_bit(&s->rc, &probs[i])) {
+                               symbol = (symbol << 1) + 1;
+                               offset &= match_bit;
+                       } else {
+                               symbol <<= 1;
+                               offset &= ~match_bit;
+                       }
+               } while (symbol < 0x100);
+       }
+
+       dict_put(&s->dict, (uint8_t)symbol);
+       lzma_state_literal(&s->lzma.state);
+}
+
+/* Decode the length of the match into s->lzma.len. */
+static void lzma_len(struct xz_dec_lzma2 *s, struct lzma_len_dec *l,
+                    uint32_t pos_state)
+{
+       uint16_t *probs;
+       uint32_t limit;
+
+       if (!rc_bit(&s->rc, &l->choice)) {
+               probs = l->low[pos_state];
+               limit = LEN_LOW_SYMBOLS;
+               s->lzma.len = MATCH_LEN_MIN;
+       } else {
+               if (!rc_bit(&s->rc, &l->choice2)) {
+                       probs = l->mid[pos_state];
+                       limit = LEN_MID_SYMBOLS;
+                       s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS;
+               } else {
+                       probs = l->high;
+                       limit = LEN_HIGH_SYMBOLS;
+                       s->lzma.len = MATCH_LEN_MIN + LEN_LOW_SYMBOLS
+                                       + LEN_MID_SYMBOLS;
+               }
+       }
+
+       s->lzma.len += rc_bittree(&s->rc, probs, limit) - limit;
+}
+
+/* Decode a match. The distance will be stored in s->lzma.rep0. */
+static void lzma_match(struct xz_dec_lzma2 *s, uint32_t pos_state)
+{
+       uint16_t *probs;
+       uint32_t dist_slot;
+       uint32_t limit;
+
+       lzma_state_match(&s->lzma.state);
+
+       s->lzma.rep3 = s->lzma.rep2;
+       s->lzma.rep2 = s->lzma.rep1;
+       s->lzma.rep1 = s->lzma.rep0;
+
+       lzma_len(s, &s->lzma.match_len_dec, pos_state);
+
+       probs = s->lzma.dist_slot[lzma_get_dist_state(s->lzma.len)];
+       dist_slot = rc_bittree(&s->rc, probs, DIST_SLOTS) - DIST_SLOTS;
+
+       if (dist_slot < DIST_MODEL_START) {
+               s->lzma.rep0 = dist_slot;
+       } else {
+               limit = (dist_slot >> 1) - 1;
+               s->lzma.rep0 = 2 + (dist_slot & 1);
+
+               if (dist_slot < DIST_MODEL_END) {
+                       s->lzma.rep0 <<= limit;
+                       probs = s->lzma.dist_special + s->lzma.rep0
+                                       - dist_slot - 1;
+                       rc_bittree_reverse(&s->rc, probs,
+                                       &s->lzma.rep0, limit);
+               } else {
+                       rc_direct(&s->rc, &s->lzma.rep0, limit - ALIGN_BITS);
+                       s->lzma.rep0 <<= ALIGN_BITS;
+                       rc_bittree_reverse(&s->rc, s->lzma.dist_align,
+                                       &s->lzma.rep0, ALIGN_BITS);
+               }
+       }
+}
+
+/*
+ * Decode a repeated match. The distance is one of the four most recently
+ * seen matches. The distance will be stored in s->lzma.rep0.
+ */
+static void lzma_rep_match(struct xz_dec_lzma2 *s, uint32_t pos_state)
+{
+       uint32_t tmp;
+
+       if (!rc_bit(&s->rc, &s->lzma.is_rep0[s->lzma.state])) {
+               if (!rc_bit(&s->rc, &s->lzma.is_rep0_long[
+                               s->lzma.state][pos_state])) {
+                       lzma_state_short_rep(&s->lzma.state);
+                       s->lzma.len = 1;
+                       return;
+               }
+       } else {
+               if (!rc_bit(&s->rc, &s->lzma.is_rep1[s->lzma.state])) {
+                       tmp = s->lzma.rep1;
+               } else {
+                       if (!rc_bit(&s->rc, &s->lzma.is_rep2[s->lzma.state])) {
+                               tmp = s->lzma.rep2;
+                       } else {
+                               tmp = s->lzma.rep3;
+                               s->lzma.rep3 = s->lzma.rep2;
+                       }
+
+                       s->lzma.rep2 = s->lzma.rep1;
+               }
+
+               s->lzma.rep1 = s->lzma.rep0;
+               s->lzma.rep0 = tmp;
+       }
+
+       lzma_state_long_rep(&s->lzma.state);
+       lzma_len(s, &s->lzma.rep_len_dec, pos_state);
+}
+
+/* LZMA decoder core */
+static bool lzma_main(struct xz_dec_lzma2 *s)
+{
+       uint32_t pos_state;
+
+       /*
+        * If the dictionary was reached during the previous call, try to
+        * finish the possibly pending repeat in the dictionary.
+        */
+       if (dict_has_space(&s->dict) && s->lzma.len > 0)
+               dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0);
+
+       /*
+        * Decode more LZMA symbols. One iteration may consume up to
+        * LZMA_IN_REQUIRED - 1 bytes.
+        */
+       while (dict_has_space(&s->dict) && !rc_limit_exceeded(&s->rc)) {
+               pos_state = s->dict.pos & s->lzma.pos_mask;
+
+               if (!rc_bit(&s->rc, &s->lzma.is_match[
+                               s->lzma.state][pos_state])) {
+                       lzma_literal(s);
+               } else {
+                       if (rc_bit(&s->rc, &s->lzma.is_rep[s->lzma.state]))
+                               lzma_rep_match(s, pos_state);
+                       else
+                               lzma_match(s, pos_state);
+
+                       if (!dict_repeat(&s->dict, &s->lzma.len, s->lzma.rep0))
+                               return false;
+               }
+       }
+
+       /*
+        * Having the range decoder always normalized when we are outside
+        * this function makes it easier to correctly handle end of the chunk.
+        */
+       rc_normalize(&s->rc);
+
+       return true;
+}
+
+/*
+ * Reset the LZMA decoder and range decoder state. Dictionary is nore reset
+ * here, because LZMA state may be reset without resetting the dictionary.
+ */
+static void lzma_reset(struct xz_dec_lzma2 *s)
+{
+       uint16_t *probs;
+       size_t i;
+
+       s->lzma.state = STATE_LIT_LIT;
+       s->lzma.rep0 = 0;
+       s->lzma.rep1 = 0;
+       s->lzma.rep2 = 0;
+       s->lzma.rep3 = 0;
+
+       /*
+        * All probabilities are initialized to the same value. This hack
+        * makes the code smaller by avoiding a separate loop for each
+        * probability array.
+        *
+        * This could be optimized so that only that part of literal
+        * probabilities that are actually required. In the common case
+        * we would write 12 KiB less.
+        */
+       probs = s->lzma.is_match[0];
+       for (i = 0; i < PROBS_TOTAL; ++i)
+               probs[i] = RC_BIT_MODEL_TOTAL / 2;
+
+       rc_reset(&s->rc);
+}
+
+/*
+ * Decode and validate LZMA properties (lc/lp/pb) and calculate the bit masks
+ * from the decoded lp and pb values. On success, the LZMA decoder state is
+ * reset and true is returned.
+ */
+static bool lzma_props(struct xz_dec_lzma2 *s, uint8_t props)
+{
+       if (props > (4 * 5 + 4) * 9 + 8)
+               return false;
+
+       s->lzma.pos_mask = 0;
+       while (props >= 9 * 5) {
+               props -= 9 * 5;
+               ++s->lzma.pos_mask;
+       }
+
+       s->lzma.pos_mask = (1 << s->lzma.pos_mask) - 1;
+
+       s->lzma.literal_pos_mask = 0;
+       while (props >= 9) {
+               props -= 9;
+               ++s->lzma.literal_pos_mask;
+       }
+
+       s->lzma.lc = props;
+
+       if (s->lzma.lc + s->lzma.literal_pos_mask > 4)
+               return false;
+
+       s->lzma.literal_pos_mask = (1 << s->lzma.literal_pos_mask) - 1;
+
+       lzma_reset(s);
+
+       return true;
+}
+
+/*********
+ * LZMA2 *
+ *********/
+
+/*
+ * The LZMA decoder assumes that if the input limit (s->rc.in_limit) hasn't
+ * been exceeded, it is safe to read up to LZMA_IN_REQUIRED bytes. This
+ * wrapper function takes care of making the LZMA decoder's assumption safe.
+ *
+ * As long as there is plenty of input left to be decoded in the current LZMA
+ * chunk, we decode directly from the caller-supplied input buffer until
+ * there's LZMA_IN_REQUIRED bytes left. Those remaining bytes are copied into
+ * s->temp.buf, which (hopefully) gets filled on the next call to this
+ * function. We decode a few bytes from the temporary buffer so that we can
+ * continue decoding from the caller-supplied input buffer again.
+ */
+static bool lzma2_lzma(struct xz_dec_lzma2 *s, struct xz_buf *b)
+{
+       size_t in_avail;
+       uint32_t tmp;
+
+       in_avail = b->in_size - b->in_pos;
+       if (s->temp.size > 0 || s->lzma2.compressed == 0) {
+               tmp = 2 * LZMA_IN_REQUIRED - s->temp.size;
+               if (tmp > s->lzma2.compressed - s->temp.size)
+                       tmp = s->lzma2.compressed - s->temp.size;
+               if (tmp > in_avail)
+                       tmp = in_avail;
+
+               memcpy(s->temp.buf + s->temp.size, b->in + b->in_pos, tmp);
+
+               if (s->temp.size + tmp == s->lzma2.compressed) {
+                       memzero(s->temp.buf + s->temp.size + tmp,
+                                       sizeof(s->temp.buf)
+                                               - s->temp.size - tmp);
+                       s->rc.in_limit = s->temp.size + tmp;
+               } else if (s->temp.size + tmp < LZMA_IN_REQUIRED) {
+                       s->temp.size += tmp;
+                       b->in_pos += tmp;
+                       return true;
+               } else {
+                       s->rc.in_limit = s->temp.size + tmp - LZMA_IN_REQUIRED;
+               }
+
+               s->rc.in = s->temp.buf;
+               s->rc.in_pos = 0;
+
+               if (!lzma_main(s) || s->rc.in_pos > s->temp.size + tmp)
+                       return false;
+
+               s->lzma2.compressed -= s->rc.in_pos;
+
+               if (s->rc.in_pos < s->temp.size) {
+                       s->temp.size -= s->rc.in_pos;
+                       memmove(s->temp.buf, s->temp.buf + s->rc.in_pos,
+                                       s->temp.size);
+                       return true;
+               }
+
+               b->in_pos += s->rc.in_pos - s->temp.size;
+               s->temp.size = 0;
+       }
+
+       in_avail = b->in_size - b->in_pos;
+       if (in_avail >= LZMA_IN_REQUIRED) {
+               s->rc.in = b->in;
+               s->rc.in_pos = b->in_pos;
+
+               if (in_avail >= s->lzma2.compressed + LZMA_IN_REQUIRED)
+                       s->rc.in_limit = b->in_pos + s->lzma2.compressed;
+               else
+                       s->rc.in_limit = b->in_size - LZMA_IN_REQUIRED;
+
+               if (!lzma_main(s))
+                       return false;
+
+               in_avail = s->rc.in_pos - b->in_pos;
+               if (in_avail > s->lzma2.compressed)
+                       return false;
+
+               s->lzma2.compressed -= in_avail;
+               b->in_pos = s->rc.in_pos;
+       }
+
+       in_avail = b->in_size - b->in_pos;
+       if (in_avail < LZMA_IN_REQUIRED) {
+               if (in_avail > s->lzma2.compressed)
+                       in_avail = s->lzma2.compressed;
+
+               memcpy(s->temp.buf, b->in + b->in_pos, in_avail);
+               s->temp.size = in_avail;
+               b->in_pos += in_avail;
+       }
+
+       return true;
+}
+
+/*
+ * Take care of the LZMA2 control layer, and forward the job of actual LZMA
+ * decoding or copying of uncompressed chunks to other functions.
+ */
+XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s,
+                                      struct xz_buf *b)
+{
+       uint32_t tmp;
+
+       while (b->in_pos < b->in_size || s->lzma2.sequence == SEQ_LZMA_RUN) {
+               switch (s->lzma2.sequence) {
+               case SEQ_CONTROL:
+                       /*
+                        * LZMA2 control byte
+                        *
+                        * Exact values:
+                        *   0x00   End marker
+                        *   0x01   Dictionary reset followed by
+                        *          an uncompressed chunk
+                        *   0x02   Uncompressed chunk (no dictionary reset)
+                        *
+                        * Highest three bits (s->control & 0xE0):
+                        *   0xE0   Dictionary reset, new properties and state
+                        *          reset, followed by LZMA compressed chunk
+                        *   0xC0   New properties and state reset, followed
+                        *          by LZMA compressed chunk (no dictionary
+                        *          reset)
+                        *   0xA0   State reset using old properties,
+                        *          followed by LZMA compressed chunk (no
+                        *          dictionary reset)
+                        *   0x80   LZMA chunk (no dictionary or state reset)
+                        *
+                        * For LZMA compressed chunks, the lowest five bits
+                        * (s->control & 1F) are the highest bits of the
+                        * uncompressed size (bits 16-20).
+                        *
+                        * A new LZMA2 stream must begin with a dictionary
+                        * reset. The first LZMA chunk must set new
+                        * properties and reset the LZMA state.
+                        *
+                        * Values that don't match anything described above
+                        * are invalid and we return XZ_DATA_ERROR.
+                        */
+                       tmp = b->in[b->in_pos++];
+
+                       if (tmp == 0x00)
+                               return XZ_STREAM_END;
+
+                       if (tmp >= 0xE0 || tmp == 0x01) {
+                               s->lzma2.need_props = true;
+                               s->lzma2.need_dict_reset = false;
+                               dict_reset(&s->dict, b);
+                       } else if (s->lzma2.need_dict_reset) {
+                               return XZ_DATA_ERROR;
+                       }
+
+                       if (tmp >= 0x80) {
+                               s->lzma2.uncompressed = (tmp & 0x1F) << 16;
+                               s->lzma2.sequence = SEQ_UNCOMPRESSED_1;
+
+                               if (tmp >= 0xC0) {
+                                       /*
+                                        * When there are new properties,
+                                        * state reset is done at
+                                        * SEQ_PROPERTIES.
+                                        */
+                                       s->lzma2.need_props = false;
+                                       s->lzma2.next_sequence
+                                                       = SEQ_PROPERTIES;
+
+                               } else if (s->lzma2.need_props) {
+                                       return XZ_DATA_ERROR;
+
+                               } else {
+                                       s->lzma2.next_sequence
+                                                       = SEQ_LZMA_PREPARE;
+                                       if (tmp >= 0xA0)
+                                               lzma_reset(s);
+                               }
+                       } else {
+                               if (tmp > 0x02)
+                                       return XZ_DATA_ERROR;
+
+                               s->lzma2.sequence = SEQ_COMPRESSED_0;
+                               s->lzma2.next_sequence = SEQ_COPY;
+                       }
+
+                       break;
+
+               case SEQ_UNCOMPRESSED_1:
+                       s->lzma2.uncompressed
+                                       += (uint32_t)b->in[b->in_pos++] << 8;
+                       s->lzma2.sequence = SEQ_UNCOMPRESSED_2;
+                       break;
+
+               case SEQ_UNCOMPRESSED_2:
+                       s->lzma2.uncompressed
+                                       += (uint32_t)b->in[b->in_pos++] + 1;
+                       s->lzma2.sequence = SEQ_COMPRESSED_0;
+                       break;
+
+               case SEQ_COMPRESSED_0:
+                       s->lzma2.compressed
+                                       = (uint32_t)b->in[b->in_pos++] << 8;
+                       s->lzma2.sequence = SEQ_COMPRESSED_1;
+                       break;
+
+               case SEQ_COMPRESSED_1:
+                       s->lzma2.compressed
+                                       += (uint32_t)b->in[b->in_pos++] + 1;
+                       s->lzma2.sequence = s->lzma2.next_sequence;
+                       break;
+
+               case SEQ_PROPERTIES:
+                       if (!lzma_props(s, b->in[b->in_pos++]))
+                               return XZ_DATA_ERROR;
+
+                       s->lzma2.sequence = SEQ_LZMA_PREPARE;
+
+               case SEQ_LZMA_PREPARE:
+                       if (s->lzma2.compressed < RC_INIT_BYTES)
+                               return XZ_DATA_ERROR;
+
+                       if (!rc_read_init(&s->rc, b))
+                               return XZ_OK;
+
+                       s->lzma2.compressed -= RC_INIT_BYTES;
+                       s->lzma2.sequence = SEQ_LZMA_RUN;
+
+               case SEQ_LZMA_RUN:
+                       /*
+                        * Set dictionary limit to indicate how much we want
+                        * to be encoded at maximum. Decode new data into the
+                        * dictionary. Flush the new data from dictionary to
+                        * b->out. Check if we finished decoding this chunk.
+                        * In case the dictionary got full but we didn't fill
+                        * the output buffer yet, we may run this loop
+                        * multiple times without changing s->lzma2.sequence.
+                        */
+                       dict_limit(&s->dict, min_t(size_t,
+                                       b->out_size - b->out_pos,
+                                       s->lzma2.uncompressed));
+                       if (!lzma2_lzma(s, b))
+                               return XZ_DATA_ERROR;
+
+                       s->lzma2.uncompressed -= dict_flush(&s->dict, b);
+
+                       if (s->lzma2.uncompressed == 0) {
+                               if (s->lzma2.compressed > 0 || s->lzma.len > 0
+                                               || !rc_is_finished(&s->rc))
+                                       return XZ_DATA_ERROR;
+
+                               rc_reset(&s->rc);
+                               s->lzma2.sequence = SEQ_CONTROL;
+
+                       } else if (b->out_pos == b->out_size
+                                       || (b->in_pos == b->in_size
+                                               && s->temp.size
+                                               < s->lzma2.compressed)) {
+                               return XZ_OK;
+                       }
+
+                       break;
+
+               case SEQ_COPY:
+                       dict_uncompressed(&s->dict, b, &s->lzma2.compressed);
+                       if (s->lzma2.compressed > 0)
+                               return XZ_OK;
+
+                       s->lzma2.sequence = SEQ_CONTROL;
+                       break;
+               }
+       }
+
+       return XZ_OK;
+}
+
+XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,
+                                                  uint32_t dict_max)
+{
+       struct xz_dec_lzma2 *s = kmalloc(sizeof(*s), GFP_KERNEL);
+       if (s == NULL)
+               return NULL;
+
+       s->dict.mode = mode;
+       s->dict.size_max = dict_max;
+
+       if (DEC_IS_PREALLOC(mode)) {
+               s->dict.buf = vmalloc(dict_max);
+               if (s->dict.buf == NULL) {
+                       kfree(s);
+                       return NULL;
+               }
+       } else if (DEC_IS_DYNALLOC(mode)) {
+               s->dict.buf = NULL;
+               s->dict.allocated = 0;
+       }
+
+       return s;
+}
+
+XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s, uint8_t props)
+{
+       /* This limits dictionary size to 3 GiB to keep parsing simpler. */
+       if (props > 39)
+               return XZ_OPTIONS_ERROR;
+
+       s->dict.size = 2 + (props & 1);
+       s->dict.size <<= (props >> 1) + 11;
+
+       if (DEC_IS_MULTI(s->dict.mode)) {
+               if (s->dict.size > s->dict.size_max)
+                       return XZ_MEMLIMIT_ERROR;
+
+               s->dict.end = s->dict.size;
+
+               if (DEC_IS_DYNALLOC(s->dict.mode)) {
+                       if (s->dict.allocated < s->dict.size) {
+                               vfree(s->dict.buf);
+                               s->dict.buf = vmalloc(s->dict.size);
+                               if (s->dict.buf == NULL) {
+                                       s->dict.allocated = 0;
+                                       return XZ_MEM_ERROR;
+                               }
+                       }
+               }
+       }
+
+       s->lzma.len = 0;
+
+       s->lzma2.sequence = SEQ_CONTROL;
+       s->lzma2.need_dict_reset = true;
+
+       s->temp.size = 0;
+
+       return XZ_OK;
+}
+
+XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s)
+{
+       if (DEC_IS_MULTI(s->dict.mode))
+               vfree(s->dict.buf);
+
+       kfree(s);
+}
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_dec_stream.c b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_dec_stream.c
new file mode 100644 (file)
index 0000000..d652550
--- /dev/null
@@ -0,0 +1,847 @@
+/*
+ * .xz Stream decoder
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include "xz_private.h"
+#include "xz_stream.h"
+
+#ifdef XZ_USE_CRC64
+#      define IS_CRC64(check_type) ((check_type) == XZ_CHECK_CRC64)
+#else
+#      define IS_CRC64(check_type) false
+#endif
+
+/* Hash used to validate the Index field */
+struct xz_dec_hash {
+       vli_type unpadded;
+       vli_type uncompressed;
+       uint32_t crc32;
+};
+
+struct xz_dec {
+       /* Position in dec_main() */
+       enum {
+               SEQ_STREAM_HEADER,
+               SEQ_BLOCK_START,
+               SEQ_BLOCK_HEADER,
+               SEQ_BLOCK_UNCOMPRESS,
+               SEQ_BLOCK_PADDING,
+               SEQ_BLOCK_CHECK,
+               SEQ_INDEX,
+               SEQ_INDEX_PADDING,
+               SEQ_INDEX_CRC32,
+               SEQ_STREAM_FOOTER
+       } sequence;
+
+       /* Position in variable-length integers and Check fields */
+       uint32_t pos;
+
+       /* Variable-length integer decoded by dec_vli() */
+       vli_type vli;
+
+       /* Saved in_pos and out_pos */
+       size_t in_start;
+       size_t out_start;
+
+#ifdef XZ_USE_CRC64
+       /* CRC32 or CRC64 value in Block or CRC32 value in Index */
+       uint64_t crc;
+#else
+       /* CRC32 value in Block or Index */
+       uint32_t crc;
+#endif
+
+       /* Type of the integrity check calculated from uncompressed data */
+       enum xz_check check_type;
+
+       /* Operation mode */
+       enum xz_mode mode;
+
+       /*
+        * True if the next call to xz_dec_run() is allowed to return
+        * XZ_BUF_ERROR.
+        */
+       bool allow_buf_error;
+
+       /* Information stored in Block Header */
+       struct {
+               /*
+                * Value stored in the Compressed Size field, or
+                * VLI_UNKNOWN if Compressed Size is not present.
+                */
+               vli_type compressed;
+
+               /*
+                * Value stored in the Uncompressed Size field, or
+                * VLI_UNKNOWN if Uncompressed Size is not present.
+                */
+               vli_type uncompressed;
+
+               /* Size of the Block Header field */
+               uint32_t size;
+       } block_header;
+
+       /* Information collected when decoding Blocks */
+       struct {
+               /* Observed compressed size of the current Block */
+               vli_type compressed;
+
+               /* Observed uncompressed size of the current Block */
+               vli_type uncompressed;
+
+               /* Number of Blocks decoded so far */
+               vli_type count;
+
+               /*
+                * Hash calculated from the Block sizes. This is used to
+                * validate the Index field.
+                */
+               struct xz_dec_hash hash;
+       } block;
+
+       /* Variables needed when verifying the Index field */
+       struct {
+               /* Position in dec_index() */
+               enum {
+                       SEQ_INDEX_COUNT,
+                       SEQ_INDEX_UNPADDED,
+                       SEQ_INDEX_UNCOMPRESSED
+               } sequence;
+
+               /* Size of the Index in bytes */
+               vli_type size;
+
+               /* Number of Records (matches block.count in valid files) */
+               vli_type count;
+
+               /*
+                * Hash calculated from the Records (matches block.hash in
+                * valid files).
+                */
+               struct xz_dec_hash hash;
+       } index;
+
+       /*
+        * Temporary buffer needed to hold Stream Header, Block Header,
+        * and Stream Footer. The Block Header is the biggest (1 KiB)
+        * so we reserve space according to that. buf[] has to be aligned
+        * to a multiple of four bytes; the size_t variables before it
+        * should guarantee this.
+        */
+       struct {
+               size_t pos;
+               size_t size;
+               uint8_t buf[1024];
+       } temp;
+
+       struct xz_dec_lzma2 *lzma2;
+
+#ifdef XZ_DEC_BCJ
+       struct xz_dec_bcj *bcj;
+       bool bcj_active;
+#endif
+};
+
+#ifdef XZ_DEC_ANY_CHECK
+/* Sizes of the Check field with different Check IDs */
+static const uint8_t check_sizes[16] = {
+       0,
+       4, 4, 4,
+       8, 8, 8,
+       16, 16, 16,
+       32, 32, 32,
+       64, 64, 64
+};
+#endif
+
+/*
+ * Fill s->temp by copying data starting from b->in[b->in_pos]. Caller
+ * must have set s->temp.pos to indicate how much data we are supposed
+ * to copy into s->temp.buf. Return true once s->temp.pos has reached
+ * s->temp.size.
+ */
+static bool fill_temp(struct xz_dec *s, struct xz_buf *b)
+{
+       size_t copy_size = min_t(size_t,
+                       b->in_size - b->in_pos, s->temp.size - s->temp.pos);
+
+       memcpy(s->temp.buf + s->temp.pos, b->in + b->in_pos, copy_size);
+       b->in_pos += copy_size;
+       s->temp.pos += copy_size;
+
+       if (s->temp.pos == s->temp.size) {
+               s->temp.pos = 0;
+               return true;
+       }
+
+       return false;
+}
+
+/* Decode a variable-length integer (little-endian base-128 encoding) */
+static enum xz_ret dec_vli(struct xz_dec *s, const uint8_t *in,
+                          size_t *in_pos, size_t in_size)
+{
+       uint8_t byte;
+
+       if (s->pos == 0)
+               s->vli = 0;
+
+       while (*in_pos < in_size) {
+               byte = in[*in_pos];
+               ++*in_pos;
+
+               s->vli |= (vli_type)(byte & 0x7F) << s->pos;
+
+               if ((byte & 0x80) == 0) {
+                       /* Don't allow non-minimal encodings. */
+                       if (byte == 0 && s->pos != 0)
+                               return XZ_DATA_ERROR;
+
+                       s->pos = 0;
+                       return XZ_STREAM_END;
+               }
+
+               s->pos += 7;
+               if (s->pos == 7 * VLI_BYTES_MAX)
+                       return XZ_DATA_ERROR;
+       }
+
+       return XZ_OK;
+}
+
+/*
+ * Decode the Compressed Data field from a Block. Update and validate
+ * the observed compressed and uncompressed sizes of the Block so that
+ * they don't exceed the values possibly stored in the Block Header
+ * (validation assumes that no integer overflow occurs, since vli_type
+ * is normally uint64_t). Update the CRC32 or CRC64 value if presence of
+ * the CRC32 or CRC64 field was indicated in Stream Header.
+ *
+ * Once the decoding is finished, validate that the observed sizes match
+ * the sizes possibly stored in the Block Header. Update the hash and
+ * Block count, which are later used to validate the Index field.
+ */
+static enum xz_ret dec_block(struct xz_dec *s, struct xz_buf *b)
+{
+       enum xz_ret ret;
+
+       s->in_start = b->in_pos;
+       s->out_start = b->out_pos;
+
+#ifdef XZ_DEC_BCJ
+       if (s->bcj_active)
+               ret = xz_dec_bcj_run(s->bcj, s->lzma2, b);
+       else
+#endif
+               ret = xz_dec_lzma2_run(s->lzma2, b);
+
+       s->block.compressed += b->in_pos - s->in_start;
+       s->block.uncompressed += b->out_pos - s->out_start;
+
+       /*
+        * There is no need to separately check for VLI_UNKNOWN, since
+        * the observed sizes are always smaller than VLI_UNKNOWN.
+        */
+       if (s->block.compressed > s->block_header.compressed
+                       || s->block.uncompressed
+                               > s->block_header.uncompressed)
+               return XZ_DATA_ERROR;
+
+       if (s->check_type == XZ_CHECK_CRC32)
+               s->crc = xz_crc32(b->out + s->out_start,
+                               b->out_pos - s->out_start, s->crc);
+#ifdef XZ_USE_CRC64
+       else if (s->check_type == XZ_CHECK_CRC64)
+               s->crc = xz_crc64(b->out + s->out_start,
+                               b->out_pos - s->out_start, s->crc);
+#endif
+
+       if (ret == XZ_STREAM_END) {
+               if (s->block_header.compressed != VLI_UNKNOWN
+                               && s->block_header.compressed
+                                       != s->block.compressed)
+                       return XZ_DATA_ERROR;
+
+               if (s->block_header.uncompressed != VLI_UNKNOWN
+                               && s->block_header.uncompressed
+                                       != s->block.uncompressed)
+                       return XZ_DATA_ERROR;
+
+               s->block.hash.unpadded += s->block_header.size
+                               + s->block.compressed;
+
+#ifdef XZ_DEC_ANY_CHECK
+               s->block.hash.unpadded += check_sizes[s->check_type];
+#else
+               if (s->check_type == XZ_CHECK_CRC32)
+                       s->block.hash.unpadded += 4;
+               else if (IS_CRC64(s->check_type))
+                       s->block.hash.unpadded += 8;
+#endif
+
+               s->block.hash.uncompressed += s->block.uncompressed;
+               s->block.hash.crc32 = xz_crc32(
+                               (const uint8_t *)&s->block.hash,
+                               sizeof(s->block.hash), s->block.hash.crc32);
+
+               ++s->block.count;
+       }
+
+       return ret;
+}
+
+/* Update the Index size and the CRC32 value. */
+static void index_update(struct xz_dec *s, const struct xz_buf *b)
+{
+       size_t in_used = b->in_pos - s->in_start;
+       s->index.size += in_used;
+       s->crc = xz_crc32(b->in + s->in_start, in_used, s->crc);
+}
+
+/*
+ * Decode the Number of Records, Unpadded Size, and Uncompressed Size
+ * fields from the Index field. That is, Index Padding and CRC32 are not
+ * decoded by this function.
+ *
+ * This can return XZ_OK (more input needed), XZ_STREAM_END (everything
+ * successfully decoded), or XZ_DATA_ERROR (input is corrupt).
+ */
+static enum xz_ret dec_index(struct xz_dec *s, struct xz_buf *b)
+{
+       enum xz_ret ret;
+
+       do {
+               ret = dec_vli(s, b->in, &b->in_pos, b->in_size);
+               if (ret != XZ_STREAM_END) {
+                       index_update(s, b);
+                       return ret;
+               }
+
+               switch (s->index.sequence) {
+               case SEQ_INDEX_COUNT:
+                       s->index.count = s->vli;
+
+                       /*
+                        * Validate that the Number of Records field
+                        * indicates the same number of Records as
+                        * there were Blocks in the Stream.
+                        */
+                       if (s->index.count != s->block.count)
+                               return XZ_DATA_ERROR;
+
+                       s->index.sequence = SEQ_INDEX_UNPADDED;
+                       break;
+
+               case SEQ_INDEX_UNPADDED:
+                       s->index.hash.unpadded += s->vli;
+                       s->index.sequence = SEQ_INDEX_UNCOMPRESSED;
+                       break;
+
+               case SEQ_INDEX_UNCOMPRESSED:
+                       s->index.hash.uncompressed += s->vli;
+                       s->index.hash.crc32 = xz_crc32(
+                                       (const uint8_t *)&s->index.hash,
+                                       sizeof(s->index.hash),
+                                       s->index.hash.crc32);
+                       --s->index.count;
+                       s->index.sequence = SEQ_INDEX_UNPADDED;
+                       break;
+               }
+       } while (s->index.count > 0);
+
+       return XZ_STREAM_END;
+}
+
+/*
+ * Validate that the next four or eight input bytes match the value
+ * of s->crc. s->pos must be zero when starting to validate the first byte.
+ * The "bits" argument allows using the same code for both CRC32 and CRC64.
+ */
+static enum xz_ret crc_validate(struct xz_dec *s, struct xz_buf *b,
+                               uint32_t bits)
+{
+       do {
+               if (b->in_pos == b->in_size)
+                       return XZ_OK;
+
+               if (((s->crc >> s->pos) & 0xFF) != b->in[b->in_pos++])
+                       return XZ_DATA_ERROR;
+
+               s->pos += 8;
+
+       } while (s->pos < bits);
+
+       s->crc = 0;
+       s->pos = 0;
+
+       return XZ_STREAM_END;
+}
+
+#ifdef XZ_DEC_ANY_CHECK
+/*
+ * Skip over the Check field when the Check ID is not supported.
+ * Returns true once the whole Check field has been skipped over.
+ */
+static bool check_skip(struct xz_dec *s, struct xz_buf *b)
+{
+       while (s->pos < check_sizes[s->check_type]) {
+               if (b->in_pos == b->in_size)
+                       return false;
+
+               ++b->in_pos;
+               ++s->pos;
+       }
+
+       s->pos = 0;
+
+       return true;
+}
+#endif
+
+/* Decode the Stream Header field (the first 12 bytes of the .xz Stream). */
+static enum xz_ret dec_stream_header(struct xz_dec *s)
+{
+       if (!memeq(s->temp.buf, HEADER_MAGIC, HEADER_MAGIC_SIZE))
+               return XZ_FORMAT_ERROR;
+
+       if (xz_crc32(s->temp.buf + HEADER_MAGIC_SIZE, 2, 0)
+                       != get_le32(s->temp.buf + HEADER_MAGIC_SIZE + 2))
+               return XZ_DATA_ERROR;
+
+       if (s->temp.buf[HEADER_MAGIC_SIZE] != 0)
+               return XZ_OPTIONS_ERROR;
+
+       /*
+        * Of integrity checks, we support none (Check ID = 0),
+        * CRC32 (Check ID = 1), and optionally CRC64 (Check ID = 4).
+        * However, if XZ_DEC_ANY_CHECK is defined, we will accept other
+        * check types too, but then the check won't be verified and
+        * a warning (XZ_UNSUPPORTED_CHECK) will be given.
+        */
+       s->check_type = s->temp.buf[HEADER_MAGIC_SIZE + 1];
+
+#ifdef XZ_DEC_ANY_CHECK
+       if (s->check_type > XZ_CHECK_MAX)
+               return XZ_OPTIONS_ERROR;
+
+       if (s->check_type > XZ_CHECK_CRC32 && !IS_CRC64(s->check_type))
+               return XZ_UNSUPPORTED_CHECK;
+#else
+       if (s->check_type > XZ_CHECK_CRC32 && !IS_CRC64(s->check_type))
+               return XZ_OPTIONS_ERROR;
+#endif
+
+       return XZ_OK;
+}
+
+/* Decode the Stream Footer field (the last 12 bytes of the .xz Stream) */
+static enum xz_ret dec_stream_footer(struct xz_dec *s)
+{
+       if (!memeq(s->temp.buf + 10, FOOTER_MAGIC, FOOTER_MAGIC_SIZE))
+               return XZ_DATA_ERROR;
+
+       if (xz_crc32(s->temp.buf + 4, 6, 0) != get_le32(s->temp.buf))
+               return XZ_DATA_ERROR;
+
+       /*
+        * Validate Backward Size. Note that we never added the size of the
+        * Index CRC32 field to s->index.size, thus we use s->index.size / 4
+        * instead of s->index.size / 4 - 1.
+        */
+       if ((s->index.size >> 2) != get_le32(s->temp.buf + 4))
+               return XZ_DATA_ERROR;
+
+       if (s->temp.buf[8] != 0 || s->temp.buf[9] != s->check_type)
+               return XZ_DATA_ERROR;
+
+       /*
+        * Use XZ_STREAM_END instead of XZ_OK to be more convenient
+        * for the caller.
+        */
+       return XZ_STREAM_END;
+}
+
+/* Decode the Block Header and initialize the filter chain. */
+static enum xz_ret dec_block_header(struct xz_dec *s)
+{
+       enum xz_ret ret;
+
+       /*
+        * Validate the CRC32. We know that the temp buffer is at least
+        * eight bytes so this is safe.
+        */
+       s->temp.size -= 4;
+       if (xz_crc32(s->temp.buf, s->temp.size, 0)
+                       != get_le32(s->temp.buf + s->temp.size))
+               return XZ_DATA_ERROR;
+
+       s->temp.pos = 2;
+
+       /*
+        * Catch unsupported Block Flags. We support only one or two filters
+        * in the chain, so we catch that with the same test.
+        */
+#ifdef XZ_DEC_BCJ
+       if (s->temp.buf[1] & 0x3E)
+#else
+       if (s->temp.buf[1] & 0x3F)
+#endif
+               return XZ_OPTIONS_ERROR;
+
+       /* Compressed Size */
+       if (s->temp.buf[1] & 0x40) {
+               if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size)
+                                       != XZ_STREAM_END)
+                       return XZ_DATA_ERROR;
+
+               s->block_header.compressed = s->vli;
+       } else {
+               s->block_header.compressed = VLI_UNKNOWN;
+       }
+
+       /* Uncompressed Size */
+       if (s->temp.buf[1] & 0x80) {
+               if (dec_vli(s, s->temp.buf, &s->temp.pos, s->temp.size)
+                               != XZ_STREAM_END)
+                       return XZ_DATA_ERROR;
+
+               s->block_header.uncompressed = s->vli;
+       } else {
+               s->block_header.uncompressed = VLI_UNKNOWN;
+       }
+
+#ifdef XZ_DEC_BCJ
+       /* If there are two filters, the first one must be a BCJ filter. */
+       s->bcj_active = s->temp.buf[1] & 0x01;
+       if (s->bcj_active) {
+               if (s->temp.size - s->temp.pos < 2)
+                       return XZ_OPTIONS_ERROR;
+
+               ret = xz_dec_bcj_reset(s->bcj, s->temp.buf[s->temp.pos++]);
+               if (ret != XZ_OK)
+                       return ret;
+
+               /*
+                * We don't support custom start offset,
+                * so Size of Properties must be zero.
+                */
+               if (s->temp.buf[s->temp.pos++] != 0x00)
+                       return XZ_OPTIONS_ERROR;
+       }
+#endif
+
+       /* Valid Filter Flags always take at least two bytes. */
+       if (s->temp.size - s->temp.pos < 2)
+               return XZ_DATA_ERROR;
+
+       /* Filter ID = LZMA2 */
+       if (s->temp.buf[s->temp.pos++] != 0x21)
+               return XZ_OPTIONS_ERROR;
+
+       /* Size of Properties = 1-byte Filter Properties */
+       if (s->temp.buf[s->temp.pos++] != 0x01)
+               return XZ_OPTIONS_ERROR;
+
+       /* Filter Properties contains LZMA2 dictionary size. */
+       if (s->temp.size - s->temp.pos < 1)
+               return XZ_DATA_ERROR;
+
+       ret = xz_dec_lzma2_reset(s->lzma2, s->temp.buf[s->temp.pos++]);
+       if (ret != XZ_OK)
+               return ret;
+
+       /* The rest must be Header Padding. */
+       while (s->temp.pos < s->temp.size)
+               if (s->temp.buf[s->temp.pos++] != 0x00)
+                       return XZ_OPTIONS_ERROR;
+
+       s->temp.pos = 0;
+       s->block.compressed = 0;
+       s->block.uncompressed = 0;
+
+       return XZ_OK;
+}
+
+static enum xz_ret dec_main(struct xz_dec *s, struct xz_buf *b)
+{
+       enum xz_ret ret;
+
+       /*
+        * Store the start position for the case when we are in the middle
+        * of the Index field.
+        */
+       s->in_start = b->in_pos;
+
+       while (true) {
+               switch (s->sequence) {
+               case SEQ_STREAM_HEADER:
+                       /*
+                        * Stream Header is copied to s->temp, and then
+                        * decoded from there. This way if the caller
+                        * gives us only little input at a time, we can
+                        * still keep the Stream Header decoding code
+                        * simple. Similar approach is used in many places
+                        * in this file.
+                        */
+                       if (!fill_temp(s, b))
+                               return XZ_OK;
+
+                       /*
+                        * If dec_stream_header() returns
+                        * XZ_UNSUPPORTED_CHECK, it is still possible
+                        * to continue decoding if working in multi-call
+                        * mode. Thus, update s->sequence before calling
+                        * dec_stream_header().
+                        */
+                       s->sequence = SEQ_BLOCK_START;
+
+                       ret = dec_stream_header(s);
+                       if (ret != XZ_OK)
+                               return ret;
+
+               case SEQ_BLOCK_START:
+                       /* We need one byte of input to continue. */
+                       if (b->in_pos == b->in_size)
+                               return XZ_OK;
+
+                       /* See if this is the beginning of the Index field. */
+                       if (b->in[b->in_pos] == 0) {
+                               s->in_start = b->in_pos++;
+                               s->sequence = SEQ_INDEX;
+                               break;
+                       }
+
+                       /*
+                        * Calculate the size of the Block Header and
+                        * prepare to decode it.
+                        */
+                       s->block_header.size
+                               = ((uint32_t)b->in[b->in_pos] + 1) * 4;
+
+                       s->temp.size = s->block_header.size;
+                       s->temp.pos = 0;
+                       s->sequence = SEQ_BLOCK_HEADER;
+
+               case SEQ_BLOCK_HEADER:
+                       if (!fill_temp(s, b))
+                               return XZ_OK;
+
+                       ret = dec_block_header(s);
+                       if (ret != XZ_OK)
+                               return ret;
+
+                       s->sequence = SEQ_BLOCK_UNCOMPRESS;
+
+               case SEQ_BLOCK_UNCOMPRESS:
+                       ret = dec_block(s, b);
+                       if (ret != XZ_STREAM_END)
+                               return ret;
+
+                       s->sequence = SEQ_BLOCK_PADDING;
+
+               case SEQ_BLOCK_PADDING:
+                       /*
+                        * Size of Compressed Data + Block Padding
+                        * must be a multiple of four. We don't need
+                        * s->block.compressed for anything else
+                        * anymore, so we use it here to test the size
+                        * of the Block Padding field.
+                        */
+                       while (s->block.compressed & 3) {
+                               if (b->in_pos == b->in_size)
+                                       return XZ_OK;
+
+                               if (b->in[b->in_pos++] != 0)
+                                       return XZ_DATA_ERROR;
+
+                               ++s->block.compressed;
+                       }
+
+                       s->sequence = SEQ_BLOCK_CHECK;
+
+               case SEQ_BLOCK_CHECK:
+                       if (s->check_type == XZ_CHECK_CRC32) {
+                               ret = crc_validate(s, b, 32);
+                               if (ret != XZ_STREAM_END)
+                                       return ret;
+                       }
+                       else if (IS_CRC64(s->check_type)) {
+                               ret = crc_validate(s, b, 64);
+                               if (ret != XZ_STREAM_END)
+                                       return ret;
+                       }
+#ifdef XZ_DEC_ANY_CHECK
+                       else if (!check_skip(s, b)) {
+                               return XZ_OK;
+                       }
+#endif
+
+                       s->sequence = SEQ_BLOCK_START;
+                       break;
+
+               case SEQ_INDEX:
+                       ret = dec_index(s, b);
+                       if (ret != XZ_STREAM_END)
+                               return ret;
+
+                       s->sequence = SEQ_INDEX_PADDING;
+
+               case SEQ_INDEX_PADDING:
+                       while ((s->index.size + (b->in_pos - s->in_start))
+                                       & 3) {
+                               if (b->in_pos == b->in_size) {
+                                       index_update(s, b);
+                                       return XZ_OK;
+                               }
+
+                               if (b->in[b->in_pos++] != 0)
+                                       return XZ_DATA_ERROR;
+                       }
+
+                       /* Finish the CRC32 value and Index size. */
+                       index_update(s, b);
+
+                       /* Compare the hashes to validate the Index field. */
+                       if (!memeq(&s->block.hash, &s->index.hash,
+                                       sizeof(s->block.hash)))
+                               return XZ_DATA_ERROR;
+
+                       s->sequence = SEQ_INDEX_CRC32;
+
+               case SEQ_INDEX_CRC32:
+                       ret = crc_validate(s, b, 32);
+                       if (ret != XZ_STREAM_END)
+                               return ret;
+
+                       s->temp.size = STREAM_HEADER_SIZE;
+                       s->sequence = SEQ_STREAM_FOOTER;
+
+               case SEQ_STREAM_FOOTER:
+                       if (!fill_temp(s, b))
+                               return XZ_OK;
+
+                       return dec_stream_footer(s);
+               }
+       }
+
+       /* Never reached */
+}
+
+/*
+ * xz_dec_run() is a wrapper for dec_main() to handle some special cases in
+ * multi-call and single-call decoding.
+ *
+ * In multi-call mode, we must return XZ_BUF_ERROR when it seems clear that we
+ * are not going to make any progress anymore. This is to prevent the caller
+ * from calling us infinitely when the input file is truncated or otherwise
+ * corrupt. Since zlib-style API allows that the caller fills the input buffer
+ * only when the decoder doesn't produce any new output, we have to be careful
+ * to avoid returning XZ_BUF_ERROR too easily: XZ_BUF_ERROR is returned only
+ * after the second consecutive call to xz_dec_run() that makes no progress.
+ *
+ * In single-call mode, if we couldn't decode everything and no error
+ * occurred, either the input is truncated or the output buffer is too small.
+ * Since we know that the last input byte never produces any output, we know
+ * that if all the input was consumed and decoding wasn't finished, the file
+ * must be corrupt. Otherwise the output buffer has to be too small or the
+ * file is corrupt in a way that decoding it produces too big output.
+ *
+ * If single-call decoding fails, we reset b->in_pos and b->out_pos back to
+ * their original values. This is because with some filter chains there won't
+ * be any valid uncompressed data in the output buffer unless the decoding
+ * actually succeeds (that's the price to pay of using the output buffer as
+ * the workspace).
+ */
+XZ_EXTERN enum xz_ret xz_dec_run(struct xz_dec *s, struct xz_buf *b)
+{
+       size_t in_start;
+       size_t out_start;
+       enum xz_ret ret;
+
+       if (DEC_IS_SINGLE(s->mode))
+               xz_dec_reset(s);
+
+       in_start = b->in_pos;
+       out_start = b->out_pos;
+       ret = dec_main(s, b);
+
+       if (DEC_IS_SINGLE(s->mode)) {
+               if (ret == XZ_OK)
+                       ret = b->in_pos == b->in_size
+                                       ? XZ_DATA_ERROR : XZ_BUF_ERROR;
+
+               if (ret != XZ_STREAM_END) {
+                       b->in_pos = in_start;
+                       b->out_pos = out_start;
+               }
+
+       } else if (ret == XZ_OK && in_start == b->in_pos
+                       && out_start == b->out_pos) {
+               if (s->allow_buf_error)
+                       ret = XZ_BUF_ERROR;
+
+               s->allow_buf_error = true;
+       } else {
+               s->allow_buf_error = false;
+       }
+
+       return ret;
+}
+
+XZ_EXTERN struct xz_dec *xz_dec_init(enum xz_mode mode, uint32_t dict_max)
+{
+       struct xz_dec *s = kmalloc(sizeof(*s), GFP_KERNEL);
+       if (s == NULL)
+               return NULL;
+
+       s->mode = mode;
+
+#ifdef XZ_DEC_BCJ
+       s->bcj = xz_dec_bcj_create(DEC_IS_SINGLE(mode));
+       if (s->bcj == NULL)
+               goto error_bcj;
+#endif
+
+       s->lzma2 = xz_dec_lzma2_create(mode, dict_max);
+       if (s->lzma2 == NULL)
+               goto error_lzma2;
+
+       xz_dec_reset(s);
+       return s;
+
+error_lzma2:
+#ifdef XZ_DEC_BCJ
+       xz_dec_bcj_end(s->bcj);
+error_bcj:
+#endif
+       kfree(s);
+       return NULL;
+}
+
+XZ_EXTERN void xz_dec_reset(struct xz_dec *s)
+{
+       s->sequence = SEQ_STREAM_HEADER;
+       s->allow_buf_error = false;
+       s->pos = 0;
+       s->crc = 0;
+       memzero(&s->block, sizeof(s->block));
+       memzero(&s->index, sizeof(s->index));
+       s->temp.pos = 0;
+       s->temp.size = STREAM_HEADER_SIZE;
+}
+
+XZ_EXTERN void xz_dec_end(struct xz_dec *s)
+{
+       if (s != NULL) {
+               xz_dec_lzma2_end(s->lzma2);
+#ifdef XZ_DEC_BCJ
+               xz_dec_bcj_end(s->bcj);
+#endif
+               kfree(s);
+       }
+}
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_dec_syms.c b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_dec_syms.c
new file mode 100644 (file)
index 0000000..32eb3c0
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * XZ decoder module information
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include <linux/module.h>
+#include <linux/xz.h>
+
+EXPORT_SYMBOL(xz_dec_init);
+EXPORT_SYMBOL(xz_dec_reset);
+EXPORT_SYMBOL(xz_dec_run);
+EXPORT_SYMBOL(xz_dec_end);
+
+MODULE_DESCRIPTION("XZ decompressor");
+MODULE_VERSION("1.0");
+MODULE_AUTHOR("Lasse Collin <lasse.collin@tukaani.org> and Igor Pavlov");
+
+/*
+ * This code is in the public domain, but in Linux it's simplest to just
+ * say it's GPL and consider the authors as the copyright holders.
+ */
+MODULE_LICENSE("GPL");
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_dec_test.c b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_dec_test.c
new file mode 100644 (file)
index 0000000..da28a19
--- /dev/null
@@ -0,0 +1,220 @@
+/*
+ * XZ decoder tester
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/uaccess.h>
+#include <linux/crc32.h>
+#include <linux/xz.h>
+
+/* Maximum supported dictionary size */
+#define DICT_MAX (1 << 20)
+
+/* Device name to pass to register_chrdev(). */
+#define DEVICE_NAME "xz_dec_test"
+
+/* Dynamically allocated device major number */
+static int device_major;
+
+/*
+ * We reuse the same decoder state, and thus can decode only one
+ * file at a time.
+ */
+static bool device_is_open;
+
+/* XZ decoder state */
+static struct xz_dec *state;
+
+/*
+ * Return value of xz_dec_run(). We need to avoid calling xz_dec_run() after
+ * it has returned XZ_STREAM_END, so we make this static.
+ */
+static enum xz_ret ret;
+
+/*
+ * Input and output buffers. The input buffer is used as a temporary safe
+ * place for the data coming from the userspace.
+ */
+static uint8_t buffer_in[1024];
+static uint8_t buffer_out[1024];
+
+/*
+ * Structure to pass the input and output buffers to the XZ decoder.
+ * A few of the fields are never modified so we initialize them here.
+ */
+static struct xz_buf buffers = {
+       .in = buffer_in,
+       .out = buffer_out,
+       .out_size = sizeof(buffer_out)
+};
+
+/*
+ * CRC32 of uncompressed data. This is used to give the user a simple way
+ * to check that the decoder produces correct output.
+ */
+static uint32_t crc;
+
+static int xz_dec_test_open(struct inode *i, struct file *f)
+{
+       if (device_is_open)
+               return -EBUSY;
+
+       device_is_open = true;
+
+       xz_dec_reset(state);
+       ret = XZ_OK;
+       crc = 0xFFFFFFFF;
+
+       buffers.in_pos = 0;
+       buffers.in_size = 0;
+       buffers.out_pos = 0;
+
+       printk(KERN_INFO DEVICE_NAME ": opened\n");
+       return 0;
+}
+
+static int xz_dec_test_release(struct inode *i, struct file *f)
+{
+       device_is_open = false;
+
+       if (ret == XZ_OK)
+               printk(KERN_INFO DEVICE_NAME ": input was truncated\n");
+
+       printk(KERN_INFO DEVICE_NAME ": closed\n");
+       return 0;
+}
+
+/*
+ * Decode the data given to us from the userspace. CRC32 of the uncompressed
+ * data is calculated and is printed at the end of successful decoding. The
+ * uncompressed data isn't stored anywhere for further use.
+ *
+ * The .xz file must have exactly one Stream and no Stream Padding. The data
+ * after the first Stream is considered to be garbage.
+ */
+static ssize_t xz_dec_test_write(struct file *file, const char __user *buf,
+                                size_t size, loff_t *pos)
+{
+       size_t remaining;
+
+       if (ret != XZ_OK) {
+               if (size > 0)
+                       printk(KERN_INFO DEVICE_NAME ": %zu bytes of "
+                                       "garbage at the end of the file\n",
+                                       size);
+
+               return -ENOSPC;
+       }
+
+       printk(KERN_INFO DEVICE_NAME ": decoding %zu bytes of input\n",
+                       size);
+
+       remaining = size;
+       while ((remaining > 0 || buffers.out_pos == buffers.out_size)
+                       && ret == XZ_OK) {
+               if (buffers.in_pos == buffers.in_size) {
+                       buffers.in_pos = 0;
+                       buffers.in_size = min(remaining, sizeof(buffer_in));
+                       if (copy_from_user(buffer_in, buf, buffers.in_size))
+                               return -EFAULT;
+
+                       buf += buffers.in_size;
+                       remaining -= buffers.in_size;
+               }
+
+               buffers.out_pos = 0;
+               ret = xz_dec_run(state, &buffers);
+               crc = crc32(crc, buffer_out, buffers.out_pos);
+       }
+
+       switch (ret) {
+       case XZ_OK:
+               printk(KERN_INFO DEVICE_NAME ": XZ_OK\n");
+               return size;
+
+       case XZ_STREAM_END:
+               printk(KERN_INFO DEVICE_NAME ": XZ_STREAM_END, "
+                               "CRC32 = 0x%08X\n", ~crc);
+               return size - remaining - (buffers.in_size - buffers.in_pos);
+
+       case XZ_MEMLIMIT_ERROR:
+               printk(KERN_INFO DEVICE_NAME ": XZ_MEMLIMIT_ERROR\n");
+               break;
+
+       case XZ_FORMAT_ERROR:
+               printk(KERN_INFO DEVICE_NAME ": XZ_FORMAT_ERROR\n");
+               break;
+
+       case XZ_OPTIONS_ERROR:
+               printk(KERN_INFO DEVICE_NAME ": XZ_OPTIONS_ERROR\n");
+               break;
+
+       case XZ_DATA_ERROR:
+               printk(KERN_INFO DEVICE_NAME ": XZ_DATA_ERROR\n");
+               break;
+
+       case XZ_BUF_ERROR:
+               printk(KERN_INFO DEVICE_NAME ": XZ_BUF_ERROR\n");
+               break;
+
+       default:
+               printk(KERN_INFO DEVICE_NAME ": Bug detected!\n");
+               break;
+       }
+
+       return -EIO;
+}
+
+/* Allocate the XZ decoder state and register the character device. */
+static int __init xz_dec_test_init(void)
+{
+       static const struct file_operations fileops = {
+               .owner = THIS_MODULE,
+               .open = &xz_dec_test_open,
+               .release = &xz_dec_test_release,
+               .write = &xz_dec_test_write
+       };
+
+       state = xz_dec_init(XZ_PREALLOC, DICT_MAX);
+       if (state == NULL)
+               return -ENOMEM;
+
+       device_major = register_chrdev(0, DEVICE_NAME, &fileops);
+       if (device_major < 0) {
+               xz_dec_end(state);
+               return device_major;
+       }
+
+       printk(KERN_INFO DEVICE_NAME ": module loaded\n");
+       printk(KERN_INFO DEVICE_NAME ": Create a device node with "
+                       "'mknod " DEVICE_NAME " c %d 0' and write .xz files "
+                       "to it.\n", device_major);
+       return 0;
+}
+
+static void __exit xz_dec_test_exit(void)
+{
+       unregister_chrdev(device_major, DEVICE_NAME);
+       xz_dec_end(state);
+       printk(KERN_INFO DEVICE_NAME ": module unloaded\n");
+}
+
+module_init(xz_dec_test_init);
+module_exit(xz_dec_test_exit);
+
+MODULE_DESCRIPTION("XZ decompressor tester");
+MODULE_VERSION("1.0");
+MODULE_AUTHOR("Lasse Collin <lasse.collin@tukaani.org>");
+
+/*
+ * This code is in the public domain, but in Linux it's simplest to just
+ * say it's GPL and consider the authors as the copyright holders.
+ */
+MODULE_LICENSE("GPL");
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_lzma2.h b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_lzma2.h
new file mode 100644 (file)
index 0000000..071d67b
--- /dev/null
@@ -0,0 +1,204 @@
+/*
+ * LZMA2 definitions
+ *
+ * Authors: Lasse Collin <lasse.collin@tukaani.org>
+ *          Igor Pavlov <http://7-zip.org/>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_LZMA2_H
+#define XZ_LZMA2_H
+
+/* Range coder constants */
+#define RC_SHIFT_BITS 8
+#define RC_TOP_BITS 24
+#define RC_TOP_VALUE (1 << RC_TOP_BITS)
+#define RC_BIT_MODEL_TOTAL_BITS 11
+#define RC_BIT_MODEL_TOTAL (1 << RC_BIT_MODEL_TOTAL_BITS)
+#define RC_MOVE_BITS 5
+
+/*
+ * Maximum number of position states. A position state is the lowest pb
+ * number of bits of the current uncompressed offset. In some places there
+ * are different sets of probabilities for different position states.
+ */
+#define POS_STATES_MAX (1 << 4)
+
+/*
+ * This enum is used to track which LZMA symbols have occurred most recently
+ * and in which order. This information is used to predict the next symbol.
+ *
+ * Symbols:
+ *  - Literal: One 8-bit byte
+ *  - Match: Repeat a chunk of data at some distance
+ *  - Long repeat: Multi-byte match at a recently seen distance
+ *  - Short repeat: One-byte repeat at a recently seen distance
+ *
+ * The symbol names are in from STATE_oldest_older_previous. REP means
+ * either short or long repeated match, and NONLIT means any non-literal.
+ */
+enum lzma_state {
+       STATE_LIT_LIT,
+       STATE_MATCH_LIT_LIT,
+       STATE_REP_LIT_LIT,
+       STATE_SHORTREP_LIT_LIT,
+       STATE_MATCH_LIT,
+       STATE_REP_LIT,
+       STATE_SHORTREP_LIT,
+       STATE_LIT_MATCH,
+       STATE_LIT_LONGREP,
+       STATE_LIT_SHORTREP,
+       STATE_NONLIT_MATCH,
+       STATE_NONLIT_REP
+};
+
+/* Total number of states */
+#define STATES 12
+
+/* The lowest 7 states indicate that the previous state was a literal. */
+#define LIT_STATES 7
+
+/* Indicate that the latest symbol was a literal. */
+static inline void lzma_state_literal(enum lzma_state *state)
+{
+       if (*state <= STATE_SHORTREP_LIT_LIT)
+               *state = STATE_LIT_LIT;
+       else if (*state <= STATE_LIT_SHORTREP)
+               *state -= 3;
+       else
+               *state -= 6;
+}
+
+/* Indicate that the latest symbol was a match. */
+static inline void lzma_state_match(enum lzma_state *state)
+{
+       *state = *state < LIT_STATES ? STATE_LIT_MATCH : STATE_NONLIT_MATCH;
+}
+
+/* Indicate that the latest state was a long repeated match. */
+static inline void lzma_state_long_rep(enum lzma_state *state)
+{
+       *state = *state < LIT_STATES ? STATE_LIT_LONGREP : STATE_NONLIT_REP;
+}
+
+/* Indicate that the latest symbol was a short match. */
+static inline void lzma_state_short_rep(enum lzma_state *state)
+{
+       *state = *state < LIT_STATES ? STATE_LIT_SHORTREP : STATE_NONLIT_REP;
+}
+
+/* Test if the previous symbol was a literal. */
+static inline bool lzma_state_is_literal(enum lzma_state state)
+{
+       return state < LIT_STATES;
+}
+
+/* Each literal coder is divided in three sections:
+ *   - 0x001-0x0FF: Without match byte
+ *   - 0x101-0x1FF: With match byte; match bit is 0
+ *   - 0x201-0x2FF: With match byte; match bit is 1
+ *
+ * Match byte is used when the previous LZMA symbol was something else than
+ * a literal (that is, it was some kind of match).
+ */
+#define LITERAL_CODER_SIZE 0x300
+
+/* Maximum number of literal coders */
+#define LITERAL_CODERS_MAX (1 << 4)
+
+/* Minimum length of a match is two bytes. */
+#define MATCH_LEN_MIN 2
+
+/* Match length is encoded with 4, 5, or 10 bits.
+ *
+ * Length   Bits
+ *  2-9      4 = Choice=0 + 3 bits
+ * 10-17     5 = Choice=1 + Choice2=0 + 3 bits
+ * 18-273   10 = Choice=1 + Choice2=1 + 8 bits
+ */
+#define LEN_LOW_BITS 3
+#define LEN_LOW_SYMBOLS (1 << LEN_LOW_BITS)
+#define LEN_MID_BITS 3
+#define LEN_MID_SYMBOLS (1 << LEN_MID_BITS)
+#define LEN_HIGH_BITS 8
+#define LEN_HIGH_SYMBOLS (1 << LEN_HIGH_BITS)
+#define LEN_SYMBOLS (LEN_LOW_SYMBOLS + LEN_MID_SYMBOLS + LEN_HIGH_SYMBOLS)
+
+/*
+ * Maximum length of a match is 273 which is a result of the encoding
+ * described above.
+ */
+#define MATCH_LEN_MAX (MATCH_LEN_MIN + LEN_SYMBOLS - 1)
+
+/*
+ * Different sets of probabilities are used for match distances that have
+ * very short match length: Lengths of 2, 3, and 4 bytes have a separate
+ * set of probabilities for each length. The matches with longer length
+ * use a shared set of probabilities.
+ */
+#define DIST_STATES 4
+
+/*
+ * Get the index of the appropriate probability array for decoding
+ * the distance slot.
+ */
+static inline uint32_t lzma_get_dist_state(uint32_t len)
+{
+       return len < DIST_STATES + MATCH_LEN_MIN
+                       ? len - MATCH_LEN_MIN : DIST_STATES - 1;
+}
+
+/*
+ * The highest two bits of a 32-bit match distance are encoded using six bits.
+ * This six-bit value is called a distance slot. This way encoding a 32-bit
+ * value takes 6-36 bits, larger values taking more bits.
+ */
+#define DIST_SLOT_BITS 6
+#define DIST_SLOTS (1 << DIST_SLOT_BITS)
+
+/* Match distances up to 127 are fully encoded using probabilities. Since
+ * the highest two bits (distance slot) are always encoded using six bits,
+ * the distances 0-3 don't need any additional bits to encode, since the
+ * distance slot itself is the same as the actual distance. DIST_MODEL_START
+ * indicates the first distance slot where at least one additional bit is
+ * needed.
+ */
+#define DIST_MODEL_START 4
+
+/*
+ * Match distances greater than 127 are encoded in three pieces:
+ *   - distance slot: the highest two bits
+ *   - direct bits: 2-26 bits below the highest two bits
+ *   - alignment bits: four lowest bits
+ *
+ * Direct bits don't use any probabilities.
+ *
+ * The distance slot value of 14 is for distances 128-191.
+ */
+#define DIST_MODEL_END 14
+
+/* Distance slots that indicate a distance <= 127. */
+#define FULL_DISTANCES_BITS (DIST_MODEL_END / 2)
+#define FULL_DISTANCES (1 << FULL_DISTANCES_BITS)
+
+/*
+ * For match distances greater than 127, only the highest two bits and the
+ * lowest four bits (alignment) is encoded using probabilities.
+ */
+#define ALIGN_BITS 4
+#define ALIGN_SIZE (1 << ALIGN_BITS)
+#define ALIGN_MASK (ALIGN_SIZE - 1)
+
+/* Total number of all probability variables */
+#define PROBS_TOTAL (1846 + LITERAL_CODERS_MAX * LITERAL_CODER_SIZE)
+
+/*
+ * LZMA remembers the four most recent match distances. Reusing these
+ * distances tends to take less space than re-encoding the actual
+ * distance value.
+ */
+#define REPS 4
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_private.h b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_private.h
new file mode 100644 (file)
index 0000000..482b90f
--- /dev/null
@@ -0,0 +1,156 @@
+/*
+ * Private includes and definitions
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_PRIVATE_H
+#define XZ_PRIVATE_H
+
+#ifdef __KERNEL__
+#      include <linux/xz.h>
+#      include <linux/kernel.h>
+#      include <asm/unaligned.h>
+       /* XZ_PREBOOT may be defined only via decompress_unxz.c. */
+#      ifndef XZ_PREBOOT
+#              include <linux/slab.h>
+#              include <linux/vmalloc.h>
+#              include <linux/string.h>
+#              ifdef CONFIG_XZ_DEC_X86
+#                      define XZ_DEC_X86
+#              endif
+#              ifdef CONFIG_XZ_DEC_POWERPC
+#                      define XZ_DEC_POWERPC
+#              endif
+#              ifdef CONFIG_XZ_DEC_IA64
+#                      define XZ_DEC_IA64
+#              endif
+#              ifdef CONFIG_XZ_DEC_ARM
+#                      define XZ_DEC_ARM
+#              endif
+#              ifdef CONFIG_XZ_DEC_ARMTHUMB
+#                      define XZ_DEC_ARMTHUMB
+#              endif
+#              ifdef CONFIG_XZ_DEC_SPARC
+#                      define XZ_DEC_SPARC
+#              endif
+#              define memeq(a, b, size) (memcmp(a, b, size) == 0)
+#              define memzero(buf, size) memset(buf, 0, size)
+#      endif
+#      define get_le32(p) le32_to_cpup((const uint32_t *)(p))
+#else
+       /*
+        * For userspace builds, use a separate header to define the required
+        * macros and functions. This makes it easier to adapt the code into
+        * different environments and avoids clutter in the Linux kernel tree.
+        */
+#      include "xz_config.h"
+#endif
+
+/* If no specific decoding mode is requested, enable support for all modes. */
+#if !defined(XZ_DEC_SINGLE) && !defined(XZ_DEC_PREALLOC) \
+               && !defined(XZ_DEC_DYNALLOC)
+#      define XZ_DEC_SINGLE
+#      define XZ_DEC_PREALLOC
+#      define XZ_DEC_DYNALLOC
+#endif
+
+/*
+ * The DEC_IS_foo(mode) macros are used in "if" statements. If only some
+ * of the supported modes are enabled, these macros will evaluate to true or
+ * false at compile time and thus allow the compiler to omit unneeded code.
+ */
+#ifdef XZ_DEC_SINGLE
+#      define DEC_IS_SINGLE(mode) ((mode) == XZ_SINGLE)
+#else
+#      define DEC_IS_SINGLE(mode) (false)
+#endif
+
+#ifdef XZ_DEC_PREALLOC
+#      define DEC_IS_PREALLOC(mode) ((mode) == XZ_PREALLOC)
+#else
+#      define DEC_IS_PREALLOC(mode) (false)
+#endif
+
+#ifdef XZ_DEC_DYNALLOC
+#      define DEC_IS_DYNALLOC(mode) ((mode) == XZ_DYNALLOC)
+#else
+#      define DEC_IS_DYNALLOC(mode) (false)
+#endif
+
+#if !defined(XZ_DEC_SINGLE)
+#      define DEC_IS_MULTI(mode) (true)
+#elif defined(XZ_DEC_PREALLOC) || defined(XZ_DEC_DYNALLOC)
+#      define DEC_IS_MULTI(mode) ((mode) != XZ_SINGLE)
+#else
+#      define DEC_IS_MULTI(mode) (false)
+#endif
+
+/*
+ * If any of the BCJ filter decoders are wanted, define XZ_DEC_BCJ.
+ * XZ_DEC_BCJ is used to enable generic support for BCJ decoders.
+ */
+#ifndef XZ_DEC_BCJ
+#      if defined(XZ_DEC_X86) || defined(XZ_DEC_POWERPC) \
+                       || defined(XZ_DEC_IA64) || defined(XZ_DEC_ARM) \
+                       || defined(XZ_DEC_ARM) || defined(XZ_DEC_ARMTHUMB) \
+                       || defined(XZ_DEC_SPARC)
+#              define XZ_DEC_BCJ
+#      endif
+#endif
+
+/*
+ * Allocate memory for LZMA2 decoder. xz_dec_lzma2_reset() must be used
+ * before calling xz_dec_lzma2_run().
+ */
+XZ_EXTERN struct xz_dec_lzma2 *xz_dec_lzma2_create(enum xz_mode mode,
+                                                  uint32_t dict_max);
+
+/*
+ * Decode the LZMA2 properties (one byte) and reset the decoder. Return
+ * XZ_OK on success, XZ_MEMLIMIT_ERROR if the preallocated dictionary is not
+ * big enough, and XZ_OPTIONS_ERROR if props indicates something that this
+ * decoder doesn't support.
+ */
+XZ_EXTERN enum xz_ret xz_dec_lzma2_reset(struct xz_dec_lzma2 *s,
+                                        uint8_t props);
+
+/* Decode raw LZMA2 stream from b->in to b->out. */
+XZ_EXTERN enum xz_ret xz_dec_lzma2_run(struct xz_dec_lzma2 *s,
+                                      struct xz_buf *b);
+
+/* Free the memory allocated for the LZMA2 decoder. */
+XZ_EXTERN void xz_dec_lzma2_end(struct xz_dec_lzma2 *s);
+
+#ifdef XZ_DEC_BCJ
+/*
+ * Allocate memory for BCJ decoders. xz_dec_bcj_reset() must be used before
+ * calling xz_dec_bcj_run().
+ */
+XZ_EXTERN struct xz_dec_bcj *xz_dec_bcj_create(bool single_call);
+
+/*
+ * Decode the Filter ID of a BCJ filter. This implementation doesn't
+ * support custom start offsets, so no decoding of Filter Properties
+ * is needed. Returns XZ_OK if the given Filter ID is supported.
+ * Otherwise XZ_OPTIONS_ERROR is returned.
+ */
+XZ_EXTERN enum xz_ret xz_dec_bcj_reset(struct xz_dec_bcj *s, uint8_t id);
+
+/*
+ * Decode raw BCJ + LZMA2 stream. This must be used only if there actually is
+ * a BCJ filter in the chain. If the chain has only LZMA2, xz_dec_lzma2_run()
+ * must be called directly.
+ */
+XZ_EXTERN enum xz_ret xz_dec_bcj_run(struct xz_dec_bcj *s,
+                                    struct xz_dec_lzma2 *lzma2,
+                                    struct xz_buf *b);
+
+/* Free the memory allocated for the BCJ filters. */
+#define xz_dec_bcj_end(s) kfree(s)
+#endif
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_stream.h b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/lib/xz/xz_stream.h
new file mode 100644 (file)
index 0000000..66cb5a7
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * Definitions for handling the .xz file format
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_STREAM_H
+#define XZ_STREAM_H
+
+#if defined(__KERNEL__) && !XZ_INTERNAL_CRC32
+#      include <linux/crc32.h>
+#      undef crc32
+#      define xz_crc32(buf, size, crc) \
+               (~crc32_le(~(uint32_t)(crc), buf, size))
+#endif
+
+/*
+ * See the .xz file format specification at
+ * http://tukaani.org/xz/xz-file-format.txt
+ * to understand the container format.
+ */
+
+#define STREAM_HEADER_SIZE 12
+
+#define HEADER_MAGIC "\3757zXZ"
+#define HEADER_MAGIC_SIZE 6
+
+#define FOOTER_MAGIC "YZ"
+#define FOOTER_MAGIC_SIZE 2
+
+/*
+ * Variable-length integer can hold a 63-bit unsigned integer or a special
+ * value indicating that the value is unknown.
+ *
+ * Experimental: vli_type can be defined to uint32_t to save a few bytes
+ * in code size (no effect on speed). Doing so limits the uncompressed and
+ * compressed size of the file to less than 256 MiB and may also weaken
+ * error detection slightly.
+ */
+typedef uint64_t vli_type;
+
+#define VLI_MAX ((vli_type)-1 / 2)
+#define VLI_UNKNOWN ((vli_type)-1)
+
+/* Maximum encoded size of a VLI */
+#define VLI_BYTES_MAX (sizeof(vli_type) * 8 / 7)
+
+/* Integrity Check types */
+enum xz_check {
+       XZ_CHECK_NONE = 0,
+       XZ_CHECK_CRC32 = 1,
+       XZ_CHECK_CRC64 = 4,
+       XZ_CHECK_SHA256 = 10
+};
+
+/* Maximum possible Check ID */
+#define XZ_CHECK_MAX 15
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/scripts/xz_wrap.sh b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/linux/scripts/xz_wrap.sh
new file mode 100644 (file)
index 0000000..7a2d372
--- /dev/null
@@ -0,0 +1,23 @@
+#!/bin/sh
+#
+# This is a wrapper for xz to compress the kernel image using appropriate
+# compression options depending on the architecture.
+#
+# Author: Lasse Collin <lasse.collin@tukaani.org>
+#
+# This file has been put into the public domain.
+# You can do whatever you want with this file.
+#
+
+BCJ=
+LZMA2OPTS=
+
+case $SRCARCH in
+       x86)            BCJ=--x86 ;;
+       powerpc)        BCJ=--powerpc ;;
+       ia64)           BCJ=--ia64; LZMA2OPTS=pb=4 ;;
+       arm)            BCJ=--arm ;;
+       sparc)          BCJ=--sparc ;;
+esac
+
+exec xz --check=crc32 $BCJ --lzma2=$LZMA2OPTS,dict=32MiB
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/userspace/Makefile b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/userspace/Makefile
new file mode 100644 (file)
index 0000000..5bd6b28
--- /dev/null
@@ -0,0 +1,48 @@
+#
+# Makefile
+#
+# Author: Lasse Collin <lasse.collin@tukaani.org>
+#
+# This file has been put into the public domain.
+# You can do whatever you want with this file.
+#
+
+CC = gcc -std=gnu89
+BCJ_CPPFLAGS = -DXZ_DEC_X86 -DXZ_DEC_POWERPC -DXZ_DEC_IA64 \
+               -DXZ_DEC_ARM -DXZ_DEC_ARMTHUMB -DXZ_DEC_SPARC
+CPPFLAGS = -DXZ_USE_CRC64 -DXZ_DEC_ANY_CHECK
+CFLAGS = -ggdb3 -O2 -pedantic -Wall -Wextra
+RM = rm -f
+VPATH = ../linux/include/linux ../linux/lib/xz
+COMMON_SRCS = xz_crc32.c xz_crc64.c xz_dec_stream.c xz_dec_lzma2.c xz_dec_bcj.c
+COMMON_OBJS = $(COMMON_SRCS:.c=.o)
+XZMINIDEC_OBJS = xzminidec.o
+BYTETEST_OBJS = bytetest.o
+BUFTEST_OBJS = buftest.o
+BOOTTEST_OBJS = boottest.o
+XZ_HEADERS = xz.h xz_private.h xz_stream.h xz_lzma2.h xz_config.h
+PROGRAMS = xzminidec bytetest buftest boottest
+
+ALL_CPPFLAGS = -I../linux/include/linux -I. $(BCJ_CPPFLAGS) $(CPPFLAGS)
+
+all: $(PROGRAMS)
+
+%.o: %.c $(XZ_HEADERS)
+       $(CC) $(ALL_CPPFLAGS) $(CFLAGS) -c -o $@ $<
+
+xzminidec: $(COMMON_OBJS) $(XZMINIDEC_OBJS)
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(COMMON_OBJS) $(XZMINIDEC_OBJS)
+
+bytetest: $(COMMON_OBJS) $(BYTETEST_OBJS)
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(COMMON_OBJS) $(BYTETEST_OBJS)
+
+buftest: $(COMMON_OBJS) $(BUFTEST_OBJS)
+       $(CC) $(CFLAGS) $(LDFLAGS) -o $@ $(COMMON_OBJS) $(BUFTEST_OBJS)
+
+boottest: $(BOOTTEST_OBJS) $(COMMON_SRCS)
+       $(CC) $(ALL_CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o $@ $(BOOTTEST_OBJS)
+
+.PHONY: clean
+clean:
+       -$(RM) $(COMMON_OBJS) $(XZMINIDEC_OBJS) $(BUFTEST_OBJS) \
+               $(BOOTTEST_OBJS) $(PROGRAMS)
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/userspace/boottest.c b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/userspace/boottest.c
new file mode 100644 (file)
index 0000000..1aef5ed
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * Test application for xz_boot.c
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+#include <stdio.h>
+
+#define STATIC static
+#define INIT
+
+static void error(/*const*/ char *msg)
+{
+       fprintf(stderr, "%s\n", msg);
+}
+
+/* Disable the CRC64 support even if it was enabled in the Makefile. */
+#undef XZ_USE_CRC64
+
+#include "../linux/lib/decompress_unxz.c"
+
+static uint8_t in[1024 * 1024];
+static uint8_t out[1024 * 1024];
+
+static int fill(void *buf, unsigned int size)
+{
+       return fread(buf, 1, size, stdin);
+}
+
+static int flush(/*const*/ void *buf, unsigned int size)
+{
+       return fwrite(buf, 1, size, stdout);
+}
+
+static void test_buf_to_buf(void)
+{
+       size_t in_size;
+       int ret;
+       in_size = fread(in, 1, sizeof(in), stdin);
+       ret = decompress(in, in_size, NULL, NULL, out, NULL, &error);
+       /* fwrite(out, 1, FIXME, stdout); */
+       fprintf(stderr, "ret = %d\n", ret);
+}
+
+static void test_buf_to_cb(void)
+{
+       size_t in_size;
+       int in_used;
+       int ret;
+       in_size = fread(in, 1, sizeof(in), stdin);
+       ret = decompress(in, in_size, NULL, &flush, NULL, &in_used, &error);
+       fprintf(stderr, "ret = %d; in_used = %d\n", ret, in_used);
+}
+
+static void test_cb_to_cb(void)
+{
+       int ret;
+       ret = decompress(NULL, 0, &fill, &flush, NULL, NULL, &error);
+       fprintf(stderr, "ret = %d\n", ret);
+}
+
+/*
+ * Not used by Linux <= 2.6.37-rc4 and newer probably won't use it either,
+ * but this kind of use case is still required to be supported by the API.
+ */
+static void test_cb_to_buf(void)
+{
+       int in_used;
+       int ret;
+       ret = decompress(in, 0, &fill, NULL, out, &in_used, &error);
+       /* fwrite(out, 1, FIXME, stdout); */
+       fprintf(stderr, "ret = %d; in_used = %d\n", ret, in_used);
+}
+
+int main(int argc, char **argv)
+{
+       if (argc != 2)
+               fprintf(stderr, "Usage: %s [bb|bc|cc|cb]\n", argv[0]);
+       else if (strcmp(argv[1], "bb") == 0)
+               test_buf_to_buf();
+       else if (strcmp(argv[1], "bc") == 0)
+               test_buf_to_cb();
+       else if (strcmp(argv[1], "cc") == 0)
+               test_cb_to_cb();
+       else if (strcmp(argv[1], "cb") == 0)
+               test_cb_to_buf();
+       else
+               fprintf(stderr, "Usage: %s [bb|bc|cc|cb]\n", argv[0]);
+
+       return 0;
+}
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/userspace/buftest.c b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/userspace/buftest.c
new file mode 100644 (file)
index 0000000..54b780a
--- /dev/null
@@ -0,0 +1,48 @@
+/*
+ * Test application to test buffer-to-buffer decoding
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include "xz.h"
+
+#define BUFFER_SIZE (1024 * 1024)
+
+static uint8_t in[BUFFER_SIZE];
+static uint8_t out[BUFFER_SIZE];
+
+int main(void)
+{
+       struct xz_buf b;
+       struct xz_dec *s;
+       enum xz_ret ret;
+
+       xz_crc32_init();
+
+       s = xz_dec_init(XZ_SINGLE, 0);
+       if (s == NULL) {
+               fputs("Initialization failed", stderr);
+               return 1;
+       }
+
+       b.in = in;
+       b.in_pos = 0;
+       b.in_size = fread(in, 1, sizeof(in), stdin);
+       b.out = out;
+       b.out_pos = 0;
+       b.out_size = sizeof(out);
+
+       ret = xz_dec_run(s, &b);
+       xz_dec_end(s);
+
+       fwrite(out, 1, b.out_pos, stdout);
+       fprintf(stderr, "%d\n", ret);
+
+       return 0;
+}
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/userspace/bytetest.c b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/userspace/bytetest.c
new file mode 100644 (file)
index 0000000..aa48b9b
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Lazy test for the case when the output size is known
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include "xz.h"
+
+static uint8_t in[1];
+static uint8_t out[BUFSIZ];
+
+int main(int argc, char **argv)
+{
+       struct xz_buf b;
+       struct xz_dec *s;
+       enum xz_ret ret;
+       const char *msg;
+       size_t uncomp_size;
+
+       if (argc != 2) {
+               fputs("Give uncompressed size as the argument", stderr);
+               return 1;
+       }
+
+       uncomp_size = atoi(argv[1]);
+
+       xz_crc32_init();
+
+       /*
+        * Support up to 64 MiB dictionary. The actually needed memory
+        * is allocated once the headers have been parsed.
+        */
+       s = xz_dec_init(XZ_DYNALLOC, 1 << 26);
+       if (s == NULL) {
+               msg = "Memory allocation failed\n";
+               goto error;
+       }
+
+       b.in = in;
+       b.in_pos = 0;
+       b.in_size = 0;
+       b.out = out;
+       b.out_pos = 0;
+       b.out_size = uncomp_size < BUFSIZ ? uncomp_size : BUFSIZ;
+
+       while (true) {
+               if (b.in_pos == b.in_size) {
+                       b.in_size = fread(in, 1, sizeof(in), stdin);
+                       b.in_pos = 0;
+               }
+
+               ret = xz_dec_run(s, &b);
+
+               if (b.out_pos == sizeof(out)) {
+                       if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos) {
+                               msg = "Write error\n";
+                               goto error;
+                       }
+
+                       uncomp_size -= b.out_pos;
+                       b.out_pos = 0;
+                       b.out_size = uncomp_size < BUFSIZ
+                                       ? uncomp_size : BUFSIZ;
+               }
+
+               if (ret == XZ_OK)
+                       continue;
+
+#ifdef XZ_DEC_ANY_CHECK
+               if (ret == XZ_UNSUPPORTED_CHECK) {
+                       fputs(argv[0], stderr);
+                       fputs(": ", stderr);
+                       fputs("Unsupported check; not verifying "
+                                       "file integrity\n", stderr);
+                       continue;
+               }
+#endif
+
+               if (uncomp_size != b.out_pos) {
+                       msg = "Uncompressed size doesn't match\n";
+                       goto error;
+               }
+
+               if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos
+                               || fclose(stdout)) {
+                       msg = "Write error\n";
+                       goto error;
+               }
+
+               switch (ret) {
+               case XZ_STREAM_END:
+                       xz_dec_end(s);
+                       return 0;
+
+               case XZ_MEM_ERROR:
+                       msg = "Memory allocation failed\n";
+                       goto error;
+
+               case XZ_MEMLIMIT_ERROR:
+                       msg = "Memory usage limit reached\n";
+                       goto error;
+
+               case XZ_FORMAT_ERROR:
+                       msg = "Not a .xz file\n";
+                       goto error;
+
+               case XZ_OPTIONS_ERROR:
+                       msg = "Unsupported options in the .xz headers\n";
+                       goto error;
+
+               case XZ_DATA_ERROR:
+               case XZ_BUF_ERROR:
+                       msg = "File is corrupt\n";
+                       goto error;
+
+               default:
+                       msg = "Bug!\n";
+                       goto error;
+               }
+       }
+
+error:
+       xz_dec_end(s);
+       fputs(argv[0], stderr);
+       fputs(": ", stderr);
+       fputs(msg, stderr);
+       return 1;
+}
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/userspace/xz_config.h b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/userspace/xz_config.h
new file mode 100644 (file)
index 0000000..eb9dac1
--- /dev/null
@@ -0,0 +1,124 @@
+/*
+ * Private includes and definitions for userspace use of XZ Embedded
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+#ifndef XZ_CONFIG_H
+#define XZ_CONFIG_H
+
+/* Uncomment to enable CRC64 support. */
+/* #define XZ_USE_CRC64 */
+
+/* Uncomment as needed to enable BCJ filter decoders. */
+/* #define XZ_DEC_X86 */
+/* #define XZ_DEC_POWERPC */
+/* #define XZ_DEC_IA64 */
+/* #define XZ_DEC_ARM */
+/* #define XZ_DEC_ARMTHUMB */
+/* #define XZ_DEC_SPARC */
+
+/*
+ * MSVC doesn't support modern C but XZ Embedded is mostly C89
+ * so these are enough.
+ */
+#ifdef _MSC_VER
+typedef unsigned char bool;
+#      define true 1
+#      define false 0
+#      define inline __inline
+#else
+#      include <stdbool.h>
+#endif
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "xz.h"
+
+#define kmalloc(size, flags) malloc(size)
+#define kfree(ptr) free(ptr)
+#define vmalloc(size) malloc(size)
+#define vfree(ptr) free(ptr)
+
+#define memeq(a, b, size) (memcmp(a, b, size) == 0)
+#define memzero(buf, size) memset(buf, 0, size)
+
+#ifndef min
+#      define min(x, y) ((x) < (y) ? (x) : (y))
+#endif
+#define min_t(type, x, y) min(x, y)
+
+/*
+ * Some functions have been marked with __always_inline to keep the
+ * performance reasonable even when the compiler is optimizing for
+ * small code size. You may be able to save a few bytes by #defining
+ * __always_inline to plain inline, but don't complain if the code
+ * becomes slow.
+ *
+ * NOTE: System headers on GNU/Linux may #define this macro already,
+ * so if you want to change it, you need to #undef it first.
+ */
+#ifndef __always_inline
+#      ifdef __GNUC__
+#              define __always_inline \
+                       inline __attribute__((__always_inline__))
+#      else
+#              define __always_inline inline
+#      endif
+#endif
+
+/* Inline functions to access unaligned unsigned 32-bit integers */
+#ifndef get_unaligned_le32
+static inline uint32_t get_unaligned_le32(const uint8_t *buf)
+{
+       return (uint32_t)buf[0]
+                       | ((uint32_t)buf[1] << 8)
+                       | ((uint32_t)buf[2] << 16)
+                       | ((uint32_t)buf[3] << 24);
+}
+#endif
+
+#ifndef get_unaligned_be32
+static inline uint32_t get_unaligned_be32(const uint8_t *buf)
+{
+       return (uint32_t)(buf[0] << 24)
+                       | ((uint32_t)buf[1] << 16)
+                       | ((uint32_t)buf[2] << 8)
+                       | (uint32_t)buf[3];
+}
+#endif
+
+#ifndef put_unaligned_le32
+static inline void put_unaligned_le32(uint32_t val, uint8_t *buf)
+{
+       buf[0] = (uint8_t)val;
+       buf[1] = (uint8_t)(val >> 8);
+       buf[2] = (uint8_t)(val >> 16);
+       buf[3] = (uint8_t)(val >> 24);
+}
+#endif
+
+#ifndef put_unaligned_be32
+static inline void put_unaligned_be32(uint32_t val, uint8_t *buf)
+{
+       buf[0] = (uint8_t)(val >> 24);
+       buf[1] = (uint8_t)(val >> 16);
+       buf[2] = (uint8_t)(val >> 8);
+       buf[3] = (uint8_t)val;
+}
+#endif
+
+/*
+ * Use get_unaligned_le32() also for aligned access for simplicity. On
+ * little endian systems, #define get_le32(ptr) (*(const uint32_t *)(ptr))
+ * could save a few bytes in code size.
+ */
+#ifndef get_le32
+#      define get_le32 get_unaligned_le32
+#endif
+
+#endif
diff --git a/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/userspace/xzminidec.c b/Ventoy2Disk/Ventoy2Disk/xz-embedded-20130513/userspace/xzminidec.c
new file mode 100644 (file)
index 0000000..ba07413
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * Simple XZ decoder command line tool
+ *
+ * Author: Lasse Collin <lasse.collin@tukaani.org>
+ *
+ * This file has been put into the public domain.
+ * You can do whatever you want with this file.
+ */
+
+/*
+ * This is really limited: Not all filters from .xz format are supported,
+ * only CRC32 is supported as the integrity check, and decoding of
+ * concatenated .xz streams is not supported. Thus, you may want to look
+ * at xzdec from XZ Utils if a few KiB bigger tool is not a problem.
+ */
+
+#include <stdbool.h>
+#include <stdio.h>
+#include <string.h>
+#include "xz.h"
+
+static uint8_t in[BUFSIZ];
+static uint8_t out[BUFSIZ];
+
+int main(int argc, char **argv)
+{
+       struct xz_buf b;
+       struct xz_dec *s;
+       enum xz_ret ret;
+       const char *msg;
+
+       if (argc >= 2 && strcmp(argv[1], "--help") == 0) {
+               fputs("Uncompress a .xz file from stdin to stdout.\n"
+                               "Arguments other than `--help' are ignored.\n",
+                               stdout);
+               return 0;
+       }
+
+       xz_crc32_init();
+#ifdef XZ_USE_CRC64
+       xz_crc64_init();
+#endif
+
+       /*
+        * Support up to 64 MiB dictionary. The actually needed memory
+        * is allocated once the headers have been parsed.
+        */
+       s = xz_dec_init(XZ_DYNALLOC, 1 << 26);
+       if (s == NULL) {
+               msg = "Memory allocation failed\n";
+               goto error;
+       }
+
+       b.in = in;
+       b.in_pos = 0;
+       b.in_size = 0;
+       b.out = out;
+       b.out_pos = 0;
+       b.out_size = BUFSIZ;
+
+       while (true) {
+               if (b.in_pos == b.in_size) {
+                       b.in_size = fread(in, 1, sizeof(in), stdin);
+                       b.in_pos = 0;
+               }
+
+               ret = xz_dec_run(s, &b);
+
+               if (b.out_pos == sizeof(out)) {
+                       if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos) {
+                               msg = "Write error\n";
+                               goto error;
+                       }
+
+                       b.out_pos = 0;
+               }
+
+               if (ret == XZ_OK)
+                       continue;
+
+#ifdef XZ_DEC_ANY_CHECK
+               if (ret == XZ_UNSUPPORTED_CHECK) {
+                       fputs(argv[0], stderr);
+                       fputs(": ", stderr);
+                       fputs("Unsupported check; not verifying "
+                                       "file integrity\n", stderr);
+                       continue;
+               }
+#endif
+
+               if (fwrite(out, 1, b.out_pos, stdout) != b.out_pos
+                               || fclose(stdout)) {
+                       msg = "Write error\n";
+                       goto error;
+               }
+
+               switch (ret) {
+               case XZ_STREAM_END:
+                       xz_dec_end(s);
+                       return 0;
+
+               case XZ_MEM_ERROR:
+                       msg = "Memory allocation failed\n";
+                       goto error;
+
+               case XZ_MEMLIMIT_ERROR:
+                       msg = "Memory usage limit reached\n";
+                       goto error;
+
+               case XZ_FORMAT_ERROR:
+                       msg = "Not a .xz file\n";
+                       goto error;
+
+               case XZ_OPTIONS_ERROR:
+                       msg = "Unsupported options in the .xz headers\n";
+                       goto error;
+
+               case XZ_DATA_ERROR:
+               case XZ_BUF_ERROR:
+                       msg = "File is corrupt\n";
+                       goto error;
+
+               default:
+                       msg = "Bug!\n";
+                       goto error;
+               }
+       }
+
+error:
+       xz_dec_end(s);
+       fputs(argv[0], stderr);
+       fputs(": ", stderr);
+       fputs(msg, stderr);
+       return 1;
+}
diff --git a/VtoyTool/BabyISO/biso.c b/VtoyTool/BabyISO/biso.c
new file mode 100644 (file)
index 0000000..27249e1
--- /dev/null
@@ -0,0 +1,865 @@
+/******************************************************************************
+ * biso.c
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "biso.h"
+#include "biso_list.h"
+#include "biso_util.h"
+#include "biso_9660.h"
+#include "biso_eltorito.h"
+#include "biso_rockridge.h"
+#include "biso_joliet.h"
+#include "biso_dump.h"
+
+CONST STATIC CHAR *g_aszErrMsg[] = 
+{
+    "Success",                   /* BISO_SUCCESS */
+    "General failed",            /* BISO_ERR_FAILED */
+    "Null pointer",              /* BISO_ERR_NULL_PTR */
+    "Failed to alloc memory",    /* BISO_ERR_ALLOC_MEM */
+    "Failed to open file",       /* BISO_ERR_OPEN_FILE */
+    "Failed to read file",       /* BISO_ERR_READ_FILE */
+    "Failed to write file",      /* BISO_ERR_WRITE_FILE */
+    "Invalid iso-9660 format",   /* BISO_ERR_INVALID_ISO9660 */
+    "Unsupported block size",    /* BISO_ERR_UNSUPPORTED_BLKSIZE */
+    "Invalid parameter",         /* BISO_ERR_INVALID_PARAM */
+    "Not found",                 /* BISO_ERR_NOT_FOUND */
+    "Not record in iso file",    /* BISO_ERR_NOT_RECORD */
+    "Handle is not initialized", /* BISO_ERR_HANDLE_UNINITIALIZED */
+};
+
+int g_biso_debug = 0;
+
+VOID BISO_SetDebug(int debug)
+{
+    g_biso_debug = debug;
+}
+
+CONST CHAR * BISO_GetErrMsg(IN ULONG ulErrCode)
+{
+    if (ulErrCode > BISO_ERR_BASE)
+    {
+        ulErrCode -= BISO_ERR_BASE;
+    }
+
+    if (ulErrCode > ARRAY_SIZE(g_aszErrMsg))
+    {
+        return NULL;
+    }
+
+    return g_aszErrMsg[ulErrCode];
+}
+
+VOID BISO_GetNow(OUT BISO_DATE_S *pstTM)
+{
+    INT iTimeZone;
+    INT iLocalHour;
+    INT iGMTHour;
+    time_t ulTime;
+    struct tm *pstLocalTM = NULL;
+    struct tm *pstGMTM = NULL;
+
+    if (NULL == pstTM)
+    {
+        return;
+    }
+    
+    time(&ulTime);
+    pstGMTM = gmtime(&ulTime); 
+    iGMTHour = pstGMTM->tm_hour;
+
+    pstLocalTM = localtime(&ulTime);
+    iLocalHour = pstLocalTM->tm_hour;
+
+    iTimeZone = iLocalHour - iGMTHour;     
+    if (iTimeZone < -12) 
+    {  
+        iTimeZone += 24;
+    } 
+    else if (iTimeZone > 12) 
+    {
+        iTimeZone -= 24;
+    }
+
+    pstTM->usYear    = pstLocalTM->tm_year + 1900;
+    pstTM->ucMonth   = pstLocalTM->tm_mon  + 1;
+    pstTM->ucDay     = pstLocalTM->tm_mday;
+    pstTM->ucHour    = pstLocalTM->tm_hour;
+    pstTM->ucMin     = pstLocalTM->tm_min;
+    pstTM->ucSecond  = pstLocalTM->tm_sec;
+    pstTM->usMillSec = 0;
+    pstTM->cZone     = (CHAR)iTimeZone;
+
+    return;
+}
+
+VOID BISO_TimeConv(IN ULONG ulTime, OUT BISO_DATE_S *pstTM)
+{
+    time_t ulTm = ulTime;
+    struct tm *pstLocalTM = NULL;
+
+    pstLocalTM = localtime(&ulTm);
+    pstTM->usYear    = pstLocalTM->tm_year + 1900;
+    pstTM->ucMonth   = pstLocalTM->tm_mon  + 1;
+    pstTM->ucDay     = pstLocalTM->tm_mday;
+    pstTM->ucHour    = pstLocalTM->tm_hour;
+    pstTM->ucMin     = pstLocalTM->tm_min;
+    pstTM->ucSecond  = pstLocalTM->tm_sec;
+    pstTM->usMillSec = 0;
+    pstTM->cZone     = (CHAR)BISO_UTIL_GetTimeZone();
+    
+    return;
+}
+
+BISO_READ_S * BISO_AllocReadHandle(VOID)
+{
+    return (BISO_READ_S *)BISO_9660_CreateParser();
+}
+
+VOID BISO_FreeReadHandle(INOUT BISO_READ_S *pstRead)
+{
+    BISO_9660_DestroyParser((BISO_PARSER_S *)pstRead);
+}
+
+BOOL_T BISO_IsISOFile(IN CONST CHAR *pcFileName)
+{
+    UINT uiReadLen;
+    UINT64 ui64FileSize = 0;
+    BISO_FILE_S *pstFile = NULL;
+    BISO_VD_S stVolDesc;
+
+    /* 先看文件大小,过小的文件不可能是ISO文件 */
+    ui64FileSize = BISO_PLAT_GetFileSize(pcFileName);
+    if (ui64FileSize < BISO_SYSTEM_AREA_SIZE + sizeof(BISO_PVD_S))
+    {
+        return BOOL_FALSE;
+    }
+
+    /* 打开ISO文件 */
+    pstFile = BISO_PLAT_OpenExistFile(pcFileName);
+    if (NULL == pstFile)
+    {
+        return BOOL_FALSE;
+    }
+
+    /* 标准规定前16个逻辑扇区用来保存系统数据,VD信息从第17个扇区开始 */
+    BISO_PLAT_SeekFile(pstFile, BISO_SYSTEM_AREA_SIZE, SEEK_SET);
+
+    /* 读出VD信息 */
+    uiReadLen = (UINT)BISO_PLAT_ReadFile(pstFile, 1, sizeof(stVolDesc), &stVolDesc);
+    if (uiReadLen != sizeof(stVolDesc))
+    {
+        BISO_PLAT_CloseFile(pstFile);
+        return BOOL_FALSE;
+    }
+
+    /* 根据ID检验是否是合法的ISO-9660格式 */
+    if (0 != strncmp(stVolDesc.szId, BISO_VD_ID, strlen(BISO_VD_ID)))
+    {
+        BISO_PLAT_CloseFile(pstFile);
+        return BOOL_FALSE;
+    }
+
+    BISO_PLAT_CloseFile(pstFile);
+    return BOOL_TRUE;
+}
+
+BOOL_T BISO_HasSVD(IN CONST BISO_READ_S *pstRead)
+{
+    if (((BISO_PARSER_S *)pstRead)->pstSVD)
+    {
+        return BOOL_TRUE;
+    }
+
+    return BOOL_FALSE;
+}
+
+BOOL_T BISO_IsUDFFile(IN CONST CHAR *pcFileName)
+{
+    UINT uiReadLen;
+    UINT64 ui64FileSize = 0;
+    BISO_FILE_S *pstFile = NULL;
+    BISO_VD_S stVolDesc;
+
+    /* 先看文件大小,过小的文件不可能是ISO文件 */
+    ui64FileSize = BISO_PLAT_GetFileSize(pcFileName);
+    if (ui64FileSize < BISO_SYSTEM_AREA_SIZE + sizeof(BISO_PVD_S))
+    {
+        return BOOL_FALSE;
+    }
+
+    /* 打开ISO文件 */
+    pstFile = BISO_PLAT_OpenExistFile(pcFileName);
+    if (NULL == pstFile)
+    {
+        return BOOL_FALSE;
+    }
+
+    /* 标准规定前16个逻辑扇区用来保存系统数据,VD信息从第17个扇区开始 */
+    BISO_PLAT_SeekFile(pstFile, BISO_SYSTEM_AREA_SIZE, SEEK_SET);
+
+    do
+    {
+        /* 每次读取1个VD结构 */
+        uiReadLen = (UINT)BISO_PLAT_ReadFile(pstFile, 1, sizeof(stVolDesc), &stVolDesc);
+        if (uiReadLen != sizeof(stVolDesc))
+        {
+            BISO_PLAT_CloseFile(pstFile);
+            return BOOL_FALSE;
+        }
+    } while (BISO_VD_TYPE_END != stVolDesc.ucType);
+
+    /* 根据ID检验是否是合法的UDF格式 */
+    (VOID)BISO_PLAT_ReadFile(pstFile, 1, sizeof(stVolDesc), &stVolDesc);
+    if (0 != strncmp(stVolDesc.szId, "BEA01", strlen("BEA01")))
+    {
+        BISO_PLAT_CloseFile(pstFile);
+        return BOOL_FALSE;
+    }
+
+    /* 根据ID检验是否是合法的UDF格式 */
+    (VOID)BISO_PLAT_ReadFile(pstFile, 1, sizeof(stVolDesc), &stVolDesc);
+    if (0 != strncmp(stVolDesc.szId, "NSR02", strlen("NSR02")) &&
+        0 != strncmp(stVolDesc.szId, "NSR03", strlen("NSR03")))
+    {
+        BISO_PLAT_CloseFile(pstFile);
+        return BOOL_FALSE;
+    }
+
+    BISO_PLAT_CloseFile(pstFile);
+    return BOOL_TRUE;
+}
+
+ULONG BISO_OpenImage(IN CONST CHAR *pcFileName, OUT BISO_READ_S *pstRead)
+{
+    return BISO_9660_OpenImage(BOOL_FALSE, pcFileName, (BISO_PARSER_S *)pstRead);
+}
+
+ULONG BISO_OpenImageWithSVD(IN CONST CHAR *pcFileName, OUT BISO_READ_S *pstRead)
+{
+    return BISO_9660_OpenImage(BOOL_TRUE, pcFileName, (BISO_PARSER_S *)pstRead);
+}
+
+ULONG BISO_GetVolumeSummary
+(
+    IN CONST BISO_READ_S *pstRead, 
+    OUT BISO_VOLUME_SUMMARY_S *pstSummary
+)
+{
+    BISO_PVD_S *pstPVD = NULL;
+    BISO_PARSER_S *pstParser = NULL;
+
+    if (NULL == pstRead || NULL == pstSummary)
+    {
+        return BISO_ERR_NULL_PTR;
+    }
+
+    if (BOOL_TRUE != BISO_IS_READ_HANDLE_VALID(pstRead))
+    {
+        return BISO_ERR_INVALID_PARAM;
+    }
+
+    pstParser = (BISO_PARSER_S *)pstRead;
+    pstPVD = pstParser->pstPVD;
+
+    /* 拷贝字符串 */
+    BISO_UTIL_CopyStr(pstPVD->szVolumeId, sizeof(pstPVD->szVolumeId), pstSummary->szVolumeId);
+    BISO_UTIL_CopyStr(pstPVD->szSystemId, sizeof(pstPVD->szSystemId), pstSummary->szSystemId);
+    BISO_UTIL_CopyStr(pstPVD->szPublisherId, sizeof(pstPVD->szPublisherId), pstSummary->szPublisherId);
+    BISO_UTIL_CopyStr(pstPVD->szPreparerId, sizeof(pstPVD->szPreparerId), pstSummary->szPreparerId);
+    BISO_UTIL_CopyStr(pstPVD->szApplicationId, sizeof(pstPVD->szApplicationId), pstSummary->szApplicationId);
+    BISO_UTIL_CopyStr(pstPVD->szCopyrightFileId, sizeof(pstPVD->szCopyrightFileId), pstSummary->szCopyrightFileId);
+    BISO_UTIL_CopyStr(pstPVD->szAbstractFileId, sizeof(pstPVD->szAbstractFileId), pstSummary->szAbstractFileId);
+
+    /* 其他字段赋值 */
+    pstSummary->uiRockRidgeVer = pstParser->ucRRIPVersion;
+    pstSummary->uiJolietLevel  = BISO_GetJolietLevel(pstRead);
+    pstSummary->uiTotDirNum    = pstParser->stDirTree.pstDirStat->uiTotDirNum;
+    pstSummary->uiTotFileNum   = pstParser->stDirTree.pstDirStat->uiTotFileNum;
+    pstSummary->uiTotLinkNum   = pstParser->stDirTree.pstDirStat->uiTotLinkNum;
+
+    return BISO_SUCCESS;
+}
+
+ULONG BISO_GetDate
+(
+    IN CONST BISO_READ_S *pstRead, 
+    IN  BISO_DATE_TYPE_E enType, 
+    OUT BISO_DATE_S *pstDate
+)
+{
+    CONST CHAR *pcDate = NULL;
+    BISO_PVD_S *pstPVD = NULL;
+    
+    if ((NULL == pstRead) || (enType >= BISO_DATE_TYPE_BUTT) || (NULL == pstDate))
+    {
+        BISO_DIAG("Invalid param %p %d %p.", pstRead, enType, pstDate);
+        return BISO_ERR_INVALID_PARAM;
+    }
+
+    BISO_CHECK_READ_HANDLE(pstRead);
+    pstPVD = ((BISO_PARSER_S *)pstRead)->pstPVD;
+
+    switch (enType)
+    {
+        case BISO_DATE_TYPE_CREATE:
+        {
+            pcDate = pstPVD->szCreationDate;
+            break;
+        }
+        case BISO_DATE_TYPE_MODIFY:
+        {
+            pcDate = pstPVD->szModifyDate;
+            break;
+        }
+        case BISO_DATE_TYPE_EXPIRATION:
+        {
+            pcDate = pstPVD->szExpirationDate;
+            break;
+        }
+        case BISO_DATE_TYPE_EFFECTIVE:
+        {
+            pcDate = pstPVD->szEffectiveDate;
+            break;
+        }
+        default :
+        {
+            return BISO_ERR_INVALID_PARAM;
+        }
+    }
+
+    return BISO_9660_ParseDate84261(pcDate, pstDate);
+}
+
+/* 获取Rock Ridge扩展的Version 0: 没有使用Rock Ridge扩展  具体版本号: 一般都是1 */
+UINT BISO_GetRockRidgeVer(IN CONST BISO_READ_S *pstRead)
+{  
+    if ((NULL == pstRead) || (BOOL_TRUE != BISO_IS_READ_HANDLE_VALID(pstRead)))
+    {
+        return 0;
+    }
+
+    return ((BISO_PARSER_S *)pstRead)->ucRRIPVersion;    
+}
+
+/* 获取Joliet扩展的Level */
+UINT BISO_GetJolietLevel(IN CONST BISO_READ_S *pstRead)
+{    
+    BISO_PARSER_S *pstParser = NULL;
+
+    if ((NULL == pstRead) || (BOOL_TRUE != BISO_IS_READ_HANDLE_VALID(pstRead)))
+    {
+        return 0;
+    }
+    
+    pstParser = (BISO_PARSER_S *)pstRead;
+    if (NULL == pstParser->pstSVD)
+    {
+        return 0;
+    }
+    return BISO_JOLIET_GetLevel(pstParser->pstSVD->aucEscape);
+}
+
+BISO_HANDLE BISO_GetRoot(IN CONST BISO_READ_S *pstRead)
+{
+    BISO_PARSER_S *pstParser = (BISO_PARSER_S *)pstRead;
+    
+    if (NULL == pstParser)
+    {
+        return NULL;
+    }
+    return (BISO_HANDLE)(&pstParser->stDirTree);
+}
+
+ULONG BISO_GetFileNodeByHdl
+(
+    IN  BISO_HANDLE       hFileHdl, 
+    OUT BISO_FILE_NODE_S *pstFileNode
+)
+{
+    BISO_POSIX_INFO_S *pstPosix = NULL;
+    BISO_DIR_TREE_S *pstDirTree = (BISO_DIR_TREE_S *)hFileHdl;
+
+    if ((NULL == pstDirTree) || (NULL == pstFileNode))
+    {
+        return BISO_ERR_NULL_PTR;
+    }
+
+    pstPosix = pstDirTree->pstPosixInfo;
+
+    /* 设置类型 */
+    BISO_SET_FLAG(pstFileNode, pstDirTree);
+
+    /* 设置名称 */
+    scnprintf(pstFileNode->szName, sizeof(pstFileNode->szName), "%s", pstDirTree->szName);
+
+    /* 设置连接路径 */
+    if (BOOL_TRUE == BISO_DIR_TREE_IS_SYMLINK(pstDirTree))
+    {
+        scnprintf(pstFileNode->szLinkTgt, sizeof(pstFileNode->szLinkTgt), "%s", pstPosix->pcLinkSrc);
+    }
+
+    pstFileNode->ui64FileSize = pstDirTree->uiSize;
+    pstFileNode->ui64Seek = (UINT64)((UINT64)pstDirTree->uiExtent * BISO_BLOCK_SIZE);
+    pstFileNode->hParent = (BISO_HANDLE)(pstDirTree->pstParent);
+    pstFileNode->hCurrent = hFileHdl;
+
+    return BISO_SUCCESS;
+}
+
+ULONG BISO_GetFileNodeByName
+(
+    IN CONST BISO_READ_S *pstRead,
+    IN CONST CHAR *pcFullPath, 
+    IN UCHAR ucFollowLink,
+    OUT BISO_FILE_NODE_S *pstFileNode
+)
+{
+    UINT i = 0;
+    UINT uiDirNum = 0;
+    UINT auiDirPos[32];
+    USHORT usPos = 0;
+    USHORT usLen = 0;
+    CHAR szDirName[1024];
+    BISO_DIR_TREE_S *pstCurDir = NULL;
+    BISO_DIR_TREE_S *pstRootDir = NULL;
+    BISO_DIR_TREE_S *pstFileList = NULL;
+    BISO_PARSER_S *pstParser = (BISO_PARSER_S *)pstRead;
+
+    if ((NULL == pstRead) || (NULL == pcFullPath) || (NULL == pstFileNode))
+    {
+        return BISO_ERR_NULL_PTR;
+    }
+
+    if ('/' == pcFullPath[0])
+    {
+        return BISO_ERR_FAILED;
+    }
+
+    pstRootDir = &(pstParser->stDirTree);
+    pstCurDir = pstRootDir->pstChild;
+
+    if ((0 == pcFullPath[0]) || ((1 == strlen(pcFullPath)) && ('/' == pcFullPath[0])))
+    {
+        /* 出参赋值 */
+        memset(pstFileNode, 0, sizeof(BISO_FILE_NODE_S));        
+        BISO_SET_FLAG(pstFileNode, pstCurDir);
+        scnprintf(pstFileNode->szName, sizeof(pstFileNode->szName), "%s", pstCurDir->szName);
+        pstFileNode->hParent = 0;
+        pstFileNode->hCurrent = (BISO_HANDLE)(pstRootDir);
+        return BISO_SUCCESS;
+    }
+
+    if ((1 == uiDirNum) && (NULL != pstRootDir))
+    {
+        pstFileList = pstRootDir->pstFileList;
+        pstCurDir = pstFileList;
+        while (pstCurDir)
+        {
+            if (0 == BISO_PATH_STRCMP(pstCurDir->szName, pcFullPath))
+            {
+                goto FOUND;
+            }
+            pstCurDir = pstCurDir->pstNext;
+        }
+    }
+
+    /* 先将目录分解开 */
+    if (BISO_SUCCESS != BISO_UTIL_PathSplit(pcFullPath, &uiDirNum, auiDirPos))
+    {
+        BISO_DIAG("Failed to split path %s", pcFullPath);
+        return BISO_ERR_FAILED;
+    }
+
+    /* 依次查找每一级目录 */
+    if (pstRootDir)
+    {
+        pstCurDir = pstRootDir->pstChild;
+    } 
+    for (i = 0; (i < uiDirNum) && (NULL != pstRootDir) && (NULL != pstCurDir); i++)
+    {
+        usPos = auiDirPos[i] >> 16;
+        usLen = auiDirPos[i] & 0xFF;
+
+        memcpy(szDirName, pcFullPath + usPos, usLen);
+        szDirName[usLen] = 0;
+
+        pstCurDir = pstRootDir->pstChild;
+        pstFileList = pstRootDir->pstFileList;
+
+        /* 先查找目录 */
+        while (pstCurDir)
+        {
+            if (0 == BISO_PATH_STRCMP(pstCurDir->szName, szDirName))
+            {
+                pstRootDir = pstCurDir;
+                break;
+            }
+            pstCurDir = pstCurDir->pstNext;
+        }
+
+        /* 再查找文件 */
+        if (NULL == pstCurDir)
+        {
+            pstCurDir = pstFileList;
+            while (pstCurDir)
+            {
+                if (0 == BISO_PATH_STRCMP(pstCurDir->szName, szDirName))
+                {
+                    break;
+                }
+                pstCurDir = pstCurDir->pstNext;
+            }
+        }
+
+        if (NULL == pstCurDir)
+        {
+            return BISO_ERR_FAILED;
+        }
+
+        /* 如果是符号链接则尝试找对应的实际文件 */
+        if ((ucFollowLink > 0) && (BOOL_TRUE == BISO_DIR_TREE_IS_SYMLINK(pstCurDir)))
+        {
+            pstCurDir = BISO_UTIL_FindLinkTgt(pstCurDir);
+        }
+
+        /* 如果是文件(或者是非法链接)的话一定是最后一级 */
+        if ((NULL == pstCurDir->pstDirStat) && (i + 1 != uiDirNum))
+        {
+            return BISO_ERR_FAILED;
+        }
+    }
+
+FOUND:
+
+    if (NULL == pstCurDir)
+    {
+        return BISO_ERR_FAILED;
+    }
+    else
+    {
+        /* 出参赋值 */
+        memset(pstFileNode, 0, sizeof(BISO_FILE_NODE_S));        
+        BISO_SET_FLAG(pstFileNode, pstCurDir);
+        scnprintf(pstFileNode->szName, sizeof(pstFileNode->szName), "%s", pstCurDir->szName);
+        if (BOOL_TRUE == BISO_DIR_TREE_IS_SYMLINK(pstCurDir))
+        {
+            scnprintf(pstFileNode->szLinkTgt, sizeof(pstFileNode->szLinkTgt), "%s", 
+                      pstCurDir->pstPosixInfo->pcLinkSrc);
+        }
+        pstFileNode->ui64FileSize = pstCurDir->uiSize;
+        pstFileNode->ui64DirRecOffet = pstCurDir->ui64FileRecordOffset;
+        pstFileNode->ui64Seek = (UINT64)((UINT64)pstCurDir->uiExtent * BISO_BLOCK_SIZE);
+        pstFileNode->hParent = (BISO_HANDLE)(pstCurDir->pstParent);
+        pstFileNode->hCurrent = (BISO_HANDLE)(pstCurDir);
+        return BISO_SUCCESS;
+    }
+}
+
+ULONG BISO_GetFileNodeByExtent
+(
+    IN CONST BISO_READ_S *pstRead,
+    IN UINT uiExtent,
+    OUT BISO_FILE_NODE_S *pstFileNode
+)
+{
+    BOOL_T bFind = BOOL_FALSE;
+    BISO_QUEUE_S *pstQueue = NULL;
+    BISO_DIR_TREE_S *pstRootDir = NULL;
+    BISO_DIR_TREE_S *pstDirTree = NULL;
+    BISO_DIR_TREE_S *pstCurDir = NULL;
+    BISO_DIR_TREE_S *pstFileList = NULL;
+    BISO_PARSER_S *pstParser = (BISO_PARSER_S *)pstRead;
+
+    if ((NULL == pstRead) || (NULL == pstFileNode))
+    {
+        return BISO_ERR_NULL_PTR;
+    }
+
+    pstRootDir = &(pstParser->stDirTree);
+
+    /* 创建堆栈,同时ROOT入栈 */
+    pstQueue = BISO_QUEUE_Create();
+    BISO_QUEUE_Push(pstQueue, pstRootDir);
+
+    while (NULL != (pstDirTree = (BISO_DIR_TREE_S *)BISO_QUEUE_PopHead(pstQueue)))
+    {
+        pstCurDir = pstDirTree->pstChild;
+        while (pstCurDir)
+        {
+            BISO_QUEUE_Push(pstQueue, pstCurDir);
+            pstCurDir = pstCurDir->pstNext;
+        }
+        
+        pstFileList = pstDirTree->pstFileList;
+        pstCurDir = pstFileList;
+        while (pstCurDir)
+        {
+            if (uiExtent == pstCurDir->uiExtent)
+            {
+                while (BISO_QUEUE_PopHead(pstQueue))
+                {
+                    bFind = BOOL_TRUE;
+                }
+                break;
+            }
+            pstCurDir = pstCurDir->pstNext;
+        }
+    }
+
+    BISO_QUEUE_Destroy(pstQueue);
+    if (BOOL_TRUE != bFind)
+    {
+        return BISO_ERR_FAILED;
+    }
+    else
+    {
+        /* 出参赋值 */
+        memset(pstFileNode, 0, sizeof(BISO_FILE_NODE_S));        
+        BISO_SET_FLAG(pstFileNode, pstCurDir);
+        scnprintf(pstFileNode->szName, sizeof(pstFileNode->szName), "%s", pstCurDir->szName);
+        if (BOOL_TRUE == BISO_DIR_TREE_IS_SYMLINK(pstCurDir))
+        {
+            scnprintf(pstFileNode->szLinkTgt, sizeof(pstFileNode->szLinkTgt), "%s", 
+                      pstCurDir->pstPosixInfo->pcLinkSrc);
+        }
+        pstFileNode->ui64FileSize = pstCurDir->uiSize;
+        pstFileNode->ui64DirRecOffet = pstCurDir->ui64FileRecordOffset;
+        pstFileNode->ui64Seek = (UINT64)((UINT64)pstCurDir->uiExtent * BISO_BLOCK_SIZE);
+        pstFileNode->hParent = (BISO_HANDLE)(pstCurDir->pstParent);
+        pstFileNode->hCurrent = (BISO_HANDLE)(pstCurDir);
+        return BISO_SUCCESS;
+    }
+}
+
+
+ULONG BISO_GetSVDFileNodeByExtent
+(
+    IN CONST BISO_READ_S *pstRead,
+    IN UINT uiExtent,
+    OUT BISO_SVD_FILE_NODE_S *pstFileNode
+)
+{
+    BOOL_T bFind = BOOL_FALSE;
+    BISO_QUEUE_S *pstQueue = NULL;
+    BISO_SVD_DIR_TREE_S *pstRootDir = NULL;
+    BISO_SVD_DIR_TREE_S *pstDirTree = NULL;
+    BISO_SVD_DIR_TREE_S *pstCurDir = NULL;
+    BISO_SVD_DIR_TREE_S *pstFileList = NULL;
+    BISO_PARSER_S *pstParser = (BISO_PARSER_S *)pstRead;
+
+    if ((NULL == pstRead) || (NULL == pstFileNode))
+    {
+        return BISO_ERR_NULL_PTR;
+    }
+
+    pstRootDir = &(pstParser->stSVDDirTree);
+
+    /* 创建堆栈,同时ROOT入栈 */
+    pstQueue = BISO_QUEUE_Create();
+    BISO_QUEUE_Push(pstQueue, pstRootDir);
+
+    while (NULL != (pstDirTree = (BISO_SVD_DIR_TREE_S *)BISO_QUEUE_PopHead(pstQueue)))
+    {
+        pstCurDir = pstDirTree->pstChild;
+        while (pstCurDir)
+        {
+            BISO_QUEUE_Push(pstQueue, pstCurDir);
+            pstCurDir = pstCurDir->pstNext;
+        }
+        
+        pstFileList = pstDirTree->pstFileList;
+        pstCurDir = pstFileList;
+        while (pstCurDir)
+        {
+            if (uiExtent == pstCurDir->uiExtent)
+            {
+                while (BISO_QUEUE_PopHead(pstQueue))
+                {
+                    bFind = BOOL_TRUE;
+                }
+                break;
+            }
+            pstCurDir = pstCurDir->pstNext;
+        }
+    }
+
+    BISO_QUEUE_Destroy(pstQueue);
+    if (BOOL_TRUE != bFind)
+    {
+        return BISO_ERR_FAILED;
+    }
+    else
+    {
+        /* 出参赋值 */
+        memset(pstFileNode, 0, sizeof(BISO_SVD_FILE_NODE_S));        
+        pstFileNode->ui64FileSize = pstCurDir->uiSize;
+        pstFileNode->ui64DirRecOffet = pstCurDir->ui64FileRecordOffset;
+        pstFileNode->ui64Seek = (UINT64)((UINT64)pstCurDir->uiExtent * BISO_BLOCK_SIZE);
+        return BISO_SUCCESS;
+    }
+}
+
+ULONG BISO_GetFileTree
+(
+    IN  BISO_HANDLE  hTopDir, 
+    IN  UINT         uiFlag,
+    OUT BISO_HANDLE *phFileTree,
+    OUT UINT        *puiNodeNum
+)
+{    
+    BISO_DIR_STAT_S *pstDirStat = NULL;
+    BISO_DIR_TREE_S *pstCurNode = NULL;
+    BISO_DIR_TREE_S *pstDirTree = (BISO_DIR_TREE_S *)hTopDir;
+    
+    if ((NULL == pstDirTree) || (NULL == phFileTree) || (NULL == puiNodeNum))
+    {
+        return BISO_ERR_NULL_PTR;
+    }
+
+    pstDirStat = pstDirTree->pstDirStat;
+    if (NULL == pstDirStat)
+    {
+        return BISO_ERR_INVALID_PARAM;
+    }
+
+    *puiNodeNum = pstDirStat->uiCurDirNum + pstDirStat->uiCurFileNum + pstDirStat->uiCurLinkNum;
+
+    switch (uiFlag)
+    {
+        case BISO_TREE_FLAG_CUR:
+        {
+            pstCurNode = pstDirTree->pstChild;
+            
+            while (NULL != pstCurNode)
+            {
+                *phFileTree++ = (BISO_HANDLE)pstCurNode;
+                pstCurNode = pstCurNode->pstNext;
+            }
+
+            pstCurNode = pstDirTree->pstFileList;
+            while (NULL != pstCurNode)
+            {
+                *phFileTree++ = (BISO_HANDLE)pstCurNode;
+                pstCurNode = pstCurNode->pstNext;
+            }
+            
+            break;
+        }
+        case BISO_TREE_FLAG_DFS:
+        {
+            break;
+        }
+        case BISO_TREE_FLAG_BFS:
+        {
+            break;
+        }
+        default :
+        {
+            return BISO_ERR_INVALID_PARAM;
+        }
+    }
+
+    return BISO_SUCCESS;
+}
+
+ULONG BISO_GetDirStat
+(
+    IN  BISO_HANDLE      hTopDir, 
+    OUT BISO_DIR_STAT_S *pstDirStat
+)
+{
+    BISO_DIR_TREE_S *pstDirTree = NULL;
+    
+    if ((NULL == hTopDir) || (NULL == pstDirStat))
+    {
+        return BISO_ERR_NULL_PTR;
+    }
+
+    pstDirTree = (BISO_DIR_TREE_S *)hTopDir;
+    if (NULL == pstDirTree->pstDirStat)
+    {
+        return BISO_ERR_INVALID_PARAM;
+    }
+    
+    memcpy(pstDirStat, pstDirTree->pstDirStat, sizeof(BISO_DIR_STAT_S));
+    return BISO_SUCCESS;
+}
+
+
+VOID BISO_Fill733(IN UINT uiData, OUT VOID *pBuf)
+{
+    UINT uiSwap = 0;
+    UINT *puiData = (UINT *)pBuf;
+
+    uiSwap |= (uiData & 0xFF) << 24;
+    uiSwap |= ((uiData >> 8) & 0xFF) << 16;
+    uiSwap |= ((uiData >> 16) & 0xFF) << 8;
+    uiSwap |= (uiData >> 24) & 0xFF;
+
+#if (__BYTE_ORDER == __LITTLE_ENDIAN)
+    puiData[0] = uiData;
+    puiData[1] = uiSwap;
+#else
+    puiData[0] = uiSwap;
+    puiData[1] = uiData;
+#endif    
+}
+
+UINT BISO_Get733(IN CONST VOID *pBuf)
+{
+    UINT *puiData = (UINT *)pBuf;
+    
+#if (__BYTE_ORDER == __LITTLE_ENDIAN)
+    return puiData[0];
+#else
+    return puiData[1];
+#endif  
+}
+
+UINT BISO_GetFileOccupySize(IN UINT uiRawSize)
+{
+    UINT uiAlign = uiRawSize % BISO_SECTOR_SIZE;
+    
+    if (0 == uiAlign)
+    {
+        return uiRawSize;
+    }
+    else
+    {
+        return uiRawSize + BISO_SECTOR_SIZE - uiAlign;
+    }
+}
+
+UINT BISO_GetBootEntryNum(IN CONST BISO_READ_S *pstRead)
+{
+    return BISO_ELTORITO_GetBootEntryNum((CONST BISO_PARSER_S *)pstRead);
+}
+
+VOID BISO_DumpFileTree(IN CONST BISO_READ_S *pstRead)
+{
+    BISO_PARSER_S *pstParser = (BISO_PARSER_S *)pstRead;
+
+    if (NULL != pstParser)
+    {
+        BISO_DUMP_ShowFileTree(1, pstParser->stDirTree.pstChild);
+        BISO_DUMP_ShowFileTree(1, pstParser->stDirTree.pstFileList);
+    }
+}
+
diff --git a/VtoyTool/BabyISO/biso.h b/VtoyTool/BabyISO/biso.h
new file mode 100644 (file)
index 0000000..3b864a9
--- /dev/null
@@ -0,0 +1,421 @@
+/******************************************************************************
+ * biso.h
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __BISO_H__
+#define __BISO_H__
+
+#include "stdio.h"
+#include "stdlib.h"
+#include "string.h"
+#include "time.h"
+
+extern int g_biso_debug;
+void BISO_SetDebug(int debug);
+
+#define BISO_DIAG(fmt, ...) if(g_biso_debug) printf(fmt, ##__VA_ARGS__)
+#define BISO_DUMP   printf
+
+#ifndef STATIC
+#define STATIC    static
+#endif
+
+#ifndef CONST
+#define CONST    const
+#endif
+
+#ifndef INLINE
+#define INLINE    inline
+#endif
+
+#ifndef VOID
+#define VOID    void
+#endif
+
+#ifndef PVOID
+typedef VOID *  PVOID;
+#endif
+
+#ifndef CHAR
+#define CHAR    char
+#endif
+
+#ifndef UCHAR
+#define UCHAR   unsigned char
+#endif
+
+#ifndef SHORT
+#define SHORT   short
+#endif
+
+#ifndef USHORT
+#define USHORT    unsigned short
+#endif
+
+#ifndef LONG
+#define LONG      long
+#endif
+
+#ifndef ULONG
+#define ULONG     unsigned long
+#endif
+
+#ifndef ULONGLONG
+#define ULONGLONG     unsigned long long
+#endif
+
+
+#ifndef INT
+#define INT       int
+#endif
+
+#ifndef UINT
+#define UINT      unsigned int
+#endif
+
+#ifndef INT16
+#define INT16     short
+#endif
+
+#ifndef UINT16
+#define UINT16    unsigned short
+#endif
+
+#ifndef INT32
+#define INT32     int
+#endif
+
+#ifndef UINT32
+#define UINT32    unsigned int
+#endif
+
+#ifndef BOOL_T
+typedef USHORT  BOOL_T;
+#define BOOL_TRUE   ((BOOL_T)1)
+#define BOOL_FALSE  ((BOOL_T)0)
+#endif
+
+typedef long long  INT64;
+typedef unsigned long long UINT64;
+
+#define BISO_PATH_STRCMP  strcmp
+
+#ifndef NULL
+#define NULL      (void *)0
+#endif
+
+#ifndef IN
+#define IN
+#endif
+
+#ifndef OUT
+#define OUT
+#endif
+
+#ifndef INOUT
+#define INOUT
+#endif
+
+#define BISO_TRUE  1
+#define BISO_FALSE 0
+
+typedef VOID BISO_READ_S;
+typedef VOID BISO_WRITE_S;
+typedef CONST VOID * BISO_HANDLE;
+
+/* error code */
+#define  BISO_SUCCESS                        0
+#define  BISO_ERR_BASE                       0x1000
+#define  BISO_ERR_FAILED                    (BISO_ERR_BASE + 1)
+#define  BISO_ERR_NULL_PTR                  (BISO_ERR_BASE + 2)
+#define  BISO_ERR_ALLOC_MEM                 (BISO_ERR_BASE + 3)
+#define  BISO_ERR_OPEN_FILE                 (BISO_ERR_BASE + 4)
+#define  BISO_ERR_READ_FILE                 (BISO_ERR_BASE + 5)
+#define  BISO_ERR_WRITE_FILE                (BISO_ERR_BASE + 6)
+#define  BISO_ERR_INVALID_ISO9660           (BISO_ERR_BASE + 7)
+#define  BISO_ERR_UNSUPPORTED_BLKSIZE       (BISO_ERR_BASE + 8)
+#define  BISO_ERR_INVALID_PARAM             (BISO_ERR_BASE + 9)
+#define  BISO_ERR_NOT_FOUND                 (BISO_ERR_BASE + 10)
+#define  BISO_ERR_NOT_RECORD                (BISO_ERR_BASE + 11)
+#define  BISO_ERR_HANDLE_UNINITIALIZED      (BISO_ERR_BASE + 12)
+#define  BISO_ERR_INVALID_RRIP_SP           (BISO_ERR_BASE + 13)
+#define  BISO_ERR_ABORT                     (BISO_ERR_BASE + 14)
+
+typedef struct tagBISO_VOLUME_SUMMARY
+{
+    CHAR szVolumeId[33];       
+    CHAR szSystemId[33];       
+    CHAR szPublisherId[129];   
+    CHAR szPreparerId[129];    
+    CHAR szApplicationId[129]; 
+    CHAR szCopyrightFileId[38];
+    CHAR szAbstractFileId[38]; 
+
+    UINT uiRockRidgeVer;
+    UINT uiJolietLevel;
+    
+    UINT uiTotDirNum;
+    UINT uiTotFileNum;
+    UINT uiTotLinkNum;
+}BISO_VOLUME_SUMMARY_S;
+
+#define BISO_TREE_FLAG_CUR   1
+#define BISO_TREE_FLAG_DFS   2
+#define BISO_TREE_FLAG_BFS   3
+
+/* time */
+typedef struct tagBISO_DATE
+{
+    USHORT usYear;     
+    UCHAR  ucMonth;    
+    UCHAR  ucDay;      
+    UCHAR  ucHour;     
+    UCHAR  ucMin;      
+    UCHAR  ucSecond;   
+    USHORT usMillSec;  
+    CHAR   cZone;      
+}BISO_DATE_S;
+
+typedef enum tagBISO_DATE_TYPE
+{
+    BISO_DATE_TYPE_CREATE = 0,
+    BISO_DATE_TYPE_MODIFY,
+    BISO_DATE_TYPE_EXPIRATION,
+    BISO_DATE_TYPE_EFFECTIVE,
+    BISO_DATE_TYPE_BUTT
+}BISO_DATE_TYPE_E;
+
+/* dir stat */
+typedef struct tagBISO_DIR_STAT
+{
+    UINT   uiCurDirNum;  
+    UINT   uiCurFileNum; 
+    UINT   uiCurLinkNum; 
+    UINT   uiCurUsedSec; 
+    UINT64 ui64CurSpace; 
+    UINT   uiTotDirNum;  
+    UINT   uiTotFileNum; 
+    UINT   uiTotLinkNum; 
+    UINT64 ui64TotSpace; 
+    UINT   uiTotUsedSec; 
+}BISO_DIR_STAT_S;
+
+#define BISO_NODE_REGFILE      1
+#define BISO_NODE_SYMLINK      2
+#define BISO_NODE_DIRECTORY    4
+
+/* file tree */
+typedef struct tagBISO_FILE_NODE
+{
+    /*
+     * ucFlag
+     * BISO_NODE_REGFILE
+     * BISO_NODE_SYMLINK
+     * BISO_NODE_DIRECTORY
+     */
+    UCHAR ucFlag;
+    CHAR  szName[256];    
+    CHAR  szLinkTgt[256]; 
+    UINT64 ui64FileSize;  
+    UINT64 ui64Seek;      
+    UINT64 ui64DirRecOffet;
+    BISO_HANDLE hParent; 
+    BISO_HANDLE hCurrent;
+}BISO_FILE_NODE_S;
+
+typedef struct tagBISO_SVD_FILE_NODE
+{
+    UINT64 ui64FileSize;
+    UINT64 ui64Seek;
+    UINT64 ui64DirRecOffet;
+}BISO_SVD_FILE_NODE_S;
+
+/* timestamp type */
+#define BISO_EXTRACT_TIME_FOLLOW    1
+#define BISO_EXTRACT_TIME_SPECIFY   2
+
+typedef struct tagBISO_EXTRACT_CTRL
+{
+    UCHAR ucATimeFlag;
+    UCHAR ucMTimeFlag;
+    BISO_DATE_S stATime;
+    BISO_DATE_S stMTime;
+}BISO_EXTRACT_CTRL_S;
+
+#define BISO_EXTRACT_MSG_MAKE_DIR        1
+#define BISO_EXTRACT_MSG_CREATE_FILE     2
+#define BISO_EXTRACT_MSG_SYMLINK         3
+
+typedef struct tagBISO_EXTRACT_NOTIFY
+{
+    UINT  uiMsg;
+    ULONG ulResult;
+    CONST CHAR *pcFileName;
+}BISO_EXTRACT_NOTIFY_S;
+
+typedef ULONG (* BISO_EXTRACE_CB_PF)(IN CONST BISO_EXTRACT_NOTIFY_S *pstNotify);
+
+CONST CHAR * BISO_GetErrMsg(IN ULONG ulErrCode);
+
+VOID BISO_GetNow(OUT BISO_DATE_S *pstTM);
+
+VOID BISO_TimeConv(IN ULONG ulTime, OUT BISO_DATE_S *pstTM);
+
+BISO_READ_S * BISO_AllocReadHandle(VOID);
+
+VOID BISO_FreeReadHandle(INOUT BISO_READ_S *pstRead);
+
+BOOL_T BISO_IsISOFile(IN CONST CHAR *pcFileName);
+
+BOOL_T BISO_IsUDFFile(IN CONST CHAR *pcFileName);
+
+ULONG BISO_OpenImage(IN CONST CHAR *pcFileName, OUT BISO_READ_S *pstRead);
+ULONG BISO_OpenImageWithSVD(IN CONST CHAR *pcFileName, OUT BISO_READ_S *pstRead);
+BOOL_T BISO_HasSVD(IN CONST BISO_READ_S *pstRead);
+
+ULONG BISO_GetVolumeSummary
+(
+    IN CONST BISO_READ_S *pstRead, 
+    OUT BISO_VOLUME_SUMMARY_S *pstSummary
+);
+
+ULONG BISO_GetDate
+(
+    IN CONST BISO_READ_S *pstRead, 
+    IN  BISO_DATE_TYPE_E enType, 
+    OUT BISO_DATE_S *pstDate
+);
+
+UINT BISO_GetRockRidgeVer(IN CONST BISO_READ_S *pstRead);
+
+UINT BISO_GetJolietLevel(IN CONST BISO_READ_S *pstRead);
+
+BISO_HANDLE BISO_GetRoot(IN CONST BISO_READ_S *pstRead);
+
+ULONG BISO_GetFileNodeByHdl
+(
+    IN  BISO_HANDLE       hFileHdl, 
+    OUT BISO_FILE_NODE_S *pstFileNode
+);
+
+ULONG BISO_GetFileNodeByName
+(
+    IN CONST BISO_READ_S *pstRead,
+    IN CONST CHAR *pcFullPath, 
+    IN UCHAR ucFollowLink,
+    OUT BISO_FILE_NODE_S *pstFileNode
+);
+
+ULONG BISO_GetFileNodeByExtent
+(
+    IN CONST BISO_READ_S *pstRead,
+    IN UINT uiExtent,
+    OUT BISO_FILE_NODE_S *pstFileNode
+);
+
+ULONG BISO_GetSVDFileNodeByExtent
+(
+    IN CONST BISO_READ_S *pstRead,
+    IN UINT uiExtent,
+    OUT BISO_SVD_FILE_NODE_S *pstFileNode
+);
+
+ULONG BISO_GetFileTree
+(
+    IN  BISO_HANDLE  hTopDir, 
+    IN  UINT         uiFlag,
+    OUT BISO_HANDLE *phFileTree,
+    OUT UINT        *puiNodeNum
+);
+
+ULONG BISO_GetDirStat
+(
+    IN  BISO_HANDLE      hTopDir, 
+    OUT BISO_DIR_STAT_S *pstDirStat
+);
+
+ULONG BISO_ExtractFile
+(
+    IN CONST BISO_READ_S *pstRead,
+    IN CONST BISO_HANDLE  hTopDir,
+    IN CONST CHAR        *pcDstPath,
+    IN CONST BISO_EXTRACT_CTRL_S *pstCtrl,
+    IN BISO_EXTRACE_CB_PF pfCallBack
+);
+
+VOID BISO_Fill733(IN UINT uiData, OUT VOID *pBuf);
+UINT BISO_Get733(IN CONST VOID *pBuf);
+UINT BISO_GetFileOccupySize(IN UINT uiRawSize);
+UINT BISO_GetBootEntryNum(IN CONST BISO_READ_S *pstRead);
+
+VOID BISO_DumpVD(IN CONST BISO_READ_S *pstRead);
+
+VOID BISO_DumpPathTable(IN CONST BISO_READ_S *pstRead);
+
+VOID BISO_DumpFileTree(IN CONST BISO_READ_S *pstRead);
+
+typedef struct tagBISO_FILE
+{
+    UINT64 CurPos;
+    UINT64 FileSize;
+}BISO_FILE_S;
+
+UINT64 BISO_PLAT_GetFileSize(IN CONST CHAR *pcFileName);
+
+VOID BISO_PLAT_UTime
+(
+    IN CONST CHAR        *pcFileName,
+    IN CONST BISO_DATE_S *pstAccessTime,
+    IN CONST BISO_DATE_S *pstModifyTime
+);
+
+BOOL_T BISO_PLAT_IsPathExist(IN CONST CHAR *pcPath);
+
+BOOL_T BISO_PLAT_IsFileExist(IN CONST CHAR *pcFilePath);
+
+ULONG BISO_PLAT_MkDir(IN CONST CHAR *pcFullPath);
+
+BISO_FILE_S * BISO_PLAT_OpenExistFile(IN CONST CHAR *pcFileName);
+BISO_FILE_S * BISO_PLAT_CreateNewFile(IN CONST CHAR *pcFileName);
+
+VOID BISO_PLAT_CloseFile(IN BISO_FILE_S *pstFile);
+
+INT64 BISO_PLAT_SeekFile(BISO_FILE_S *pstFile, INT64 i64Offset, INT iFromWhere);
+
+UINT64 BISO_PLAT_ReadFile
+(
+    IN  BISO_FILE_S *pstFile,
+    IN  UINT         uiBlkSize,
+    IN  UINT         uiBlkNum,
+    OUT VOID        *pBuf
+);
+
+UINT64 BISO_PLAT_WriteFile
+(
+    IN  BISO_FILE_S *pstFile,
+    IN  UINT         uiBlkSize,
+    IN  UINT         uiBlkNum,
+    IN  VOID        *pBuf
+);
+
+CHAR * BISO_PLAT_GetCurDir(VOID);
+UINT64 BISO_UTIL_WholeFile2Buf(IN CONST CHAR *szFileName, OUT UCHAR *pucBuf);
+
+#endif /* __BISO_H__ */
+
diff --git a/VtoyTool/BabyISO/biso_9660.c b/VtoyTool/BabyISO/biso_9660.c
new file mode 100644 (file)
index 0000000..c829be7
--- /dev/null
@@ -0,0 +1,1458 @@
+/******************************************************************************
+ * biso_9660.c
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "biso.h"
+#include "biso_list.h"
+#include "biso_util.h"
+#include "biso_plat.h"
+#include "biso_9660.h"
+#include "biso_eltorito.h"
+#include "biso_rockridge.h"
+#include "biso_dump.h"
+
+STATIC ULONG BISO_9660_ReadPathTable(IN BISO_FILE_S *pstFile, OUT BISO_PARSER_S *pstParser)
+{
+    UINT64 ui64Seek = 0;
+    UINT uiReadLen = 0;
+    UCHAR *pucBuf = NULL;
+    BISO_PVD_S *pstPVD = NULL;
+
+    DBGASSERT(NULL != pstFile);
+    DBGASSERT(NULL != pstParser);
+        
+    pstPVD = pstParser->pstPVD;
+    ui64Seek = BISO_PATHTBL_LOCATION(pstPVD);
+    ui64Seek = ui64Seek * BISO_BLOCK_SIZE;
+
+    /*
+     * 申请内存用于保存Path Table
+     * 由于Path Table是连续保存的,所以这里一次性读出保存,使用时再做解析
+     * Path Table保存时是连续的,一条Path Table可以跨block, 最后一个扇区的剩余空间填0
+     */
+    pucBuf = (UCHAR *)BISO_MALLOC(pstPVD->uiPathTblSize);
+    if (NULL == pucBuf)
+    {
+        return BISO_ERR_ALLOC_MEM;
+    }
+
+    BISO_PLAT_SeekFile(pstFile, ui64Seek, SEEK_SET);
+
+    /* 读出Path Table */
+    uiReadLen = (UINT)BISO_PLAT_ReadFile(pstFile, 1, pstPVD->uiPathTblSize, pucBuf);
+    if (uiReadLen != pstPVD->uiPathTblSize)
+    {
+        BISO_FREE(pucBuf);
+        BISO_DIAG("Read Len %u, data len %u.", uiReadLen, pstPVD->uiPathTblSize);
+        return BISO_ERR_READ_FILE;
+    }
+
+    pstParser->pucPathTable = pucBuf;
+    return BISO_SUCCESS;
+}
+
+/* 深度优先文件树目录节点入栈 注意这里只是针对目录节点入栈, 文件节点没有处理 */
+VOID BISO_9660_FillDfsStack
+(
+    IN BISO_DIR_TREE_S *pstTop, 
+    INOUT BISO_QUEUE_S *pstQueue
+)
+{
+    BISO_DIR_TREE_S *pstCurDir = NULL;
+    
+    DBGASSERT(NULL != pstTop);
+    DBGASSERT(NULL != pstQueue);
+
+    /* TOP入栈 */
+    BISO_QUEUE_Push(pstQueue, pstTop);
+
+    pstCurDir = pstTop->pstChild;
+    if (NULL == pstCurDir)
+    {
+        return;
+    }
+
+    for ( ; ; )
+    {
+        /* 
+         * 按照以下顺序依次入栈:
+         * 1. 自己入栈
+         * 2. 子节点入栈
+         * 3. Next节点入栈
+         * 4. Parent的Next节点入栈
+         */
+    
+        BISO_QUEUE_Push(pstQueue, pstCurDir);
+
+        if (NULL != pstCurDir->pstChild)
+        {
+            pstCurDir = pstCurDir->pstChild;
+        }
+        else if (NULL != pstCurDir->pstNext)
+        {
+            pstCurDir = pstCurDir->pstNext;
+        }
+        else
+        {
+            /* 往上回溯, 一直找到需要入栈的节点 */
+            while ((pstTop != pstCurDir->pstParent) && (NULL == pstCurDir->pstParent->pstNext))
+            {
+                pstCurDir = pstCurDir->pstParent;
+            }
+
+            if (pstTop == pstCurDir->pstParent)
+            {
+                break;
+            }
+            pstCurDir = pstCurDir->pstParent->pstNext;
+        }
+    }
+}
+
+/* 根据Extent的值查找子目录节点  */
+STATIC BISO_DIR_TREE_S *BISO_9660_FindChild
+(
+    IN BISO_DIR_TREE_S *pstParent, 
+    IN UINT uiChildExtent
+)
+{
+    BISO_DIR_TREE_S *pstCurNode = NULL;
+    
+    DBGASSERT(NULL != pstParent);
+    
+    pstCurNode = pstParent->pstChild;
+    while (NULL != pstCurNode)
+    {
+        if (pstCurNode->uiExtent == uiChildExtent)
+        {
+            return pstCurNode;
+        }
+        pstCurNode = pstCurNode->pstNext;
+    }
+    return NULL;
+}
+
+STATIC ULONG BISO_9660_ReadVD(IN BISO_FILE_S *pstFile, OUT BISO_PARSER_S *pstParser)
+{
+    UINT uiReadLen = 0;
+    BISO_VD_S stVolDesc;
+    BISO_VD_NODE_S *pstVdNode = NULL;
+
+    DBGASSERT(NULL != pstFile);
+    DBGASSERT(NULL != pstParser);
+
+    /* 标准规定前16个逻辑扇区用来保存系统数据,VD信息从第17个扇区开始 */
+    BISO_PLAT_SeekFile(pstFile, BISO_SYSTEM_AREA_SIZE, SEEK_SET);
+
+    do
+    {
+        /* 每次读取1个VD结构 */
+        uiReadLen = (UINT)BISO_PLAT_ReadFile(pstFile, 1, sizeof(stVolDesc), &stVolDesc);
+        if (uiReadLen != sizeof(stVolDesc))
+        {
+            BISO_DIAG("Read Len %u, struct len %u.", uiReadLen, (UINT)sizeof(stVolDesc));
+            return BISO_ERR_READ_FILE;
+        }
+
+        /* 根据ID检验是否是合法的ISO-9660格式 */
+        if (0 != strncmp(stVolDesc.szId, BISO_VD_ID, strlen(BISO_VD_ID)))
+        {
+            BISO_DIAG("Invalid cdid: %02x %02x %02x %02x %02x\n", 
+                (UCHAR)stVolDesc.szId[0], (UCHAR)stVolDesc.szId[1], 
+                (UCHAR)stVolDesc.szId[2], (UCHAR)stVolDesc.szId[3], 
+                (UCHAR)stVolDesc.szId[4]);
+            return BISO_ERR_INVALID_ISO9660;
+        }
+
+        /* 申请内存保存VD信息 */
+        pstVdNode = (BISO_VD_NODE_S *)BISO_ZALLOC(sizeof(BISO_VD_NODE_S));
+        if (NULL == pstVdNode)
+        {
+            return BISO_ERR_ALLOC_MEM;
+        }
+
+        /* 链表节点挂接 */
+        memcpy(&(pstVdNode->stVD), &stVolDesc, sizeof(BISO_VD_S));
+        BISO_DLL_AddTail(&(pstParser->stVDList), (BISO_DLL_NODE_S *)pstVdNode);
+
+        switch (stVolDesc.ucType)
+        {
+            case BISO_VD_TYPE_BOOT:
+            {
+                pstParser->pstBVD = (BISO_BVD_S *)&(pstVdNode->stVD);
+                pstParser->pstBVD->uiBootCatlogStart = BISO_LTOH_UINT(pstParser->pstBVD->uiBootCatlogStart);
+                break;
+            }
+            case BISO_VD_TYPE_PVD:
+            {
+                pstParser->pstPVD = (BISO_PVD_S *)&(pstVdNode->stVD);
+                break;
+            }
+            case BISO_VD_TYPE_SVD:
+            {
+                pstParser->pstSVD = (BISO_SVD_S *)&(pstVdNode->stVD);
+                break;
+            }
+            case BISO_VD_TYPE_PART:
+            case BISO_VD_TYPE_END:
+            {
+                break;
+            }
+            default :
+            {
+                BISO_DIAG("Invalid VD type: %u\n", stVolDesc.ucType);
+                return BISO_ERR_INVALID_ISO9660;
+            }
+        }
+    } while (BISO_VD_TYPE_END != stVolDesc.ucType);
+
+    /* 标准规定必须有1个主卷描述符 */
+    if (NULL == pstParser->pstPVD)
+    {
+        BISO_DIAG("No PVD found.");
+        return BISO_ERR_INVALID_ISO9660;
+    }
+
+    /* 目前只支持逻辑块大小为2048 */
+    if (BISO_BLOCK_SIZE != pstParser->pstPVD->usBlockSize)
+    {
+        BISO_DIAG("Unsupported block size %u.", pstParser->pstPVD->usBlockSize);
+        return BISO_ERR_UNSUPPORTED_BLKSIZE;
+    }
+    
+    return BISO_SUCCESS;
+}
+
+STATIC UCHAR * BISO_9660_ReadDirRecord
+(
+    IN  BISO_FILE_S *pstFile, 
+    IN  UINT  uiExtent, 
+    OUT UINT *puiSize
+)
+{
+    UINT64 ui64Seek = 0;
+    UINT uiReadLen = 0;
+    UCHAR *pucBuf = NULL;
+    BISO_DIR_RECORD_S stCurrent;
+
+    DBGASSERT(NULL != pstFile);
+    DBGASSERT(NULL != puiSize);
+
+    /* 第一条Dir Record是Current, 先读出自己,得到总的缓冲区长度 */
+    ui64Seek = BISO_BLOCK_SIZE * (UINT64)uiExtent;
+    BISO_PLAT_SeekFile(pstFile, ui64Seek, SEEK_SET);
+    uiReadLen = (UINT)BISO_PLAT_ReadFile(pstFile, 1, sizeof(stCurrent), &stCurrent);
+    if (uiReadLen != sizeof(stCurrent))
+    {
+        BISO_DIAG("Read len %u, buf len %u.", uiReadLen, (UINT)sizeof(stCurrent));
+        return NULL;
+    }
+
+    /* 申请内存, 一次性把当前目录的Directory信息全部读出 */
+    pucBuf = (UCHAR *)BISO_MALLOC(stCurrent.uiSize);
+    if (NULL == pucBuf)
+    {
+        return NULL;
+    }
+
+    BISO_PLAT_SeekFile(pstFile, ui64Seek, SEEK_SET);
+    uiReadLen = (UINT)BISO_PLAT_ReadFile(pstFile, 1, stCurrent.uiSize, pucBuf);
+    if (uiReadLen != stCurrent.uiSize)
+    {
+        BISO_DIAG("Read len %u, buf len %u.", uiReadLen, stCurrent.uiSize);
+        BISO_FREE(pucBuf);
+        return NULL;
+    }
+
+    *puiSize = stCurrent.uiSize;
+    return pucBuf;
+}
+
+STATIC BISO_DIR_TREE_S * BISO_9660_CreateDirNode
+(
+    IN BISO_FILE_S *pstFile,
+    IN BISO_PARSER_S *pstParser,
+    IN BISO_DIR_RECORD_S  *pstRecord,
+    INOUT BISO_DIR_TREE_S *pstPre,
+    INOUT BISO_DIR_TREE_S *pstParent
+)
+{
+    BISO_DIR_TREE_S *pstNew = NULL;
+    
+    DBGASSERT(NULL != pstRecord);
+    DBGASSERT(NULL != pstPre);
+    DBGASSERT(NULL != pstParent);
+
+    /* 申请内存用于保存目录节点 */
+    pstNew = (BISO_DIR_TREE_S *)BISO_ZALLOC(sizeof(BISO_DIR_TREE_S));
+    if (NULL == pstNew)
+    {
+        return NULL;
+    }
+    
+    /* 目录节点属性赋值 */
+    BISO_UTIL_CopyStr(pstRecord->szName, pstRecord->ucNameLen, pstNew->szName);
+    pstNew->uiExtent = pstRecord->uiExtent;
+    pstNew->usNameLen = (USHORT)strlen(pstNew->szName);
+
+    /* 申请统计信息的节点 */
+    pstNew->pstDirStat = (BISO_DIR_STAT_S *)BISO_ZALLOC(sizeof(BISO_DIR_STAT_S));
+    if (NULL == pstNew->pstDirStat)
+    {
+        BISO_FREE(pstNew);
+        return NULL;
+    }
+
+    /* 挂接到父目录下 */
+    if (NULL == pstPre)
+    {
+        pstParent->pstChild = pstNew;
+    }
+    else
+    {
+        pstPre->pstNext = pstNew;
+    }
+    pstNew->pstParent = pstParent;
+
+    /* 更新父目录统计 */
+    pstParent->pstDirStat->uiCurDirNum++;
+
+    /* 更新目录的Rock Ridge扩展信息 */
+    (VOID)BISO_RRIP_ReadExtInfo(pstFile, pstParser, pstRecord, pstNew);
+    return pstNew;
+}
+
+STATIC BISO_SVD_DIR_TREE_S * BISO_9660_CreateSVDDirNode
+(
+    IN BISO_FILE_S *pstFile,
+    IN BISO_PARSER_S *pstParser,
+    IN BISO_DIR_RECORD_S  *pstRecord,
+    INOUT BISO_SVD_DIR_TREE_S *pstPre,
+    INOUT BISO_SVD_DIR_TREE_S *pstParent
+)
+{
+    BISO_SVD_DIR_TREE_S *pstNew = NULL;
+    
+    DBGASSERT(NULL != pstRecord);
+    DBGASSERT(NULL != pstPre);
+    DBGASSERT(NULL != pstParent);
+
+    /* 申请内存用于保存目录节点 */
+    pstNew = (BISO_SVD_DIR_TREE_S *)BISO_ZALLOC(sizeof(BISO_SVD_DIR_TREE_S));
+    if (NULL == pstNew)
+    {
+        return NULL;
+    }
+    
+    /* 目录节点属性赋值 */
+    pstNew->uiExtent = pstRecord->uiExtent;
+
+    /* 挂接到父目录下 */
+    if (NULL == pstPre)
+    {
+        pstParent->pstChild = pstNew;
+    }
+    else
+    {
+        pstPre->pstNext = pstNew;
+    }
+    pstNew->pstParent = pstParent;
+
+    return pstNew;
+}
+
+/* ISO原始文件格式处理 */
+STATIC ULONG BISO_9660_ProcRawFileNameFmt(INOUT CHAR *szFileName, INOUT USHORT *pusLen)
+{
+    UINT i;
+    UINT uiSepNum = 0;
+    UINT uiSepIndex = 0;
+    UINT uiDotNum = 0;
+    UINT uiLen = *pusLen;
+
+    for (i = 0; i < uiLen; i++)
+    {
+        if (szFileName[i] == ';')
+        {
+            if (uiSepNum > 0)
+            {
+                return BISO_SUCCESS;
+            }
+            uiSepNum++;
+            uiSepIndex = i;
+        }
+        else if (szFileName[i] == '.')
+        {
+            if (uiDotNum > 0)
+            {
+                return BISO_SUCCESS;
+            }
+            uiDotNum++;
+        }
+        else if (szFileName[i] >= 'a' && szFileName[i] <= 'z')
+        {
+            return BISO_SUCCESS;
+        }
+    }
+
+    /* 必须只包含1个分号和1个点号 */
+    if (uiSepNum != 1 || uiSepIndex == 0 || uiDotNum != 1)
+    {
+        return BISO_SUCCESS;
+    }
+
+    /* 分号后面是文件版本号, 纯数字 */
+    for (i = uiSepIndex + 1; i < uiLen; i++)
+    {
+        if (szFileName[i] < '0' || szFileName[i] > '9')
+        {
+            return BISO_SUCCESS;
+        }
+    }
+
+    /* 把分号后面剔除 */
+    szFileName[uiSepIndex] = 0;
+    uiLen = uiSepIndex;
+
+    /* 如果只有文件名没有扩展名则把最后的点号去掉 */
+    if (uiLen > 1 && szFileName[uiLen - 1] == '.')
+    {
+        szFileName[uiLen - 1] = 0;
+        uiLen--;
+    }
+
+    *pusLen = (USHORT)uiLen;
+
+    /* 转换为小写 */
+    for (i = 0; i < uiLen; i++)
+    {
+        if (szFileName[i] >= 'A' && szFileName[i] <= 'Z')
+        {
+            szFileName[i] = 'a' + (szFileName[i] - 'A');            
+        }
+    }
+    return BISO_SUCCESS;
+}
+
+STATIC BISO_DIR_TREE_S * BISO_9660_CreateFileNode
+(
+    IN BISO_FILE_S *pstFile,
+    IN BISO_PARSER_S *pstParser,
+    IN BISO_DIR_RECORD_S  *pstRecord,
+    INOUT BISO_DIR_TREE_S *pstPre,
+    INOUT BISO_DIR_TREE_S *pstParent
+)
+{   
+    UINT uiSecNum = 0;
+    BISO_DIR_TREE_S *pstNew = NULL;
+    
+    DBGASSERT(NULL != pstRecord);
+    DBGASSERT(NULL != pstPre);
+    DBGASSERT(NULL != pstParent);
+
+    /* 申请内存用于保存文件节点 */
+    pstNew = (BISO_DIR_TREE_S *)BISO_ZALLOC(sizeof(BISO_DIR_TREE_S));
+    if (NULL == pstNew)
+    {
+        return NULL;
+    }
+    
+    /* 目录节点属性赋值 */
+    BISO_UTIL_CopyStr(pstRecord->szName, pstRecord->ucNameLen, pstNew->szName);
+    pstNew->uiExtent = pstRecord->uiExtent;
+    pstNew->usNameLen = (USHORT)strlen(pstNew->szName);
+    pstNew->uiSize = pstRecord->uiSize;
+    
+    /* 读取文件的Rock Ridge扩展信息 */
+    (VOID)BISO_RRIP_ReadExtInfo(pstFile, pstParser, pstRecord, pstNew);
+
+    /* 更新文件所在目录的记录 */
+    if (BOOL_TRUE == BISO_DIR_TREE_IS_SYMLINK(pstNew))
+    {
+        pstParent->pstDirStat->uiCurLinkNum++;
+    }
+    else
+    {
+        uiSecNum = BISO_USED_SECTOR_NUM(pstNew->uiSize);
+        pstParent->pstDirStat->uiCurFileNum++;
+        pstParent->pstDirStat->uiCurUsedSec += uiSecNum;
+        pstParent->pstDirStat->ui64CurSpace += pstNew->uiSize;
+    }          
+
+    /* 节点挂接到当前目录的FileList上 */
+    if (NULL == pstPre)
+    {
+        pstParent->pstFileList = pstNew;
+    }
+    else
+    {
+        pstPre->pstNext = pstNew;
+    }
+    pstNew->pstParent = pstParent;
+
+    if (NULL == pstNew->pstPosixInfo && pstNew->usNameLen > 2)
+    {
+        BISO_9660_ProcRawFileNameFmt(pstNew->szName, &pstNew->usNameLen);
+    }
+    
+    return pstNew;
+}
+
+STATIC BISO_SVD_DIR_TREE_S * BISO_9660_CreateSVDFileNode
+(
+    IN BISO_FILE_S *pstFile,
+    IN BISO_PARSER_S *pstParser,
+    IN BISO_DIR_RECORD_S  *pstRecord,
+    INOUT BISO_SVD_DIR_TREE_S *pstPre,
+    INOUT BISO_SVD_DIR_TREE_S *pstParent
+)
+{   
+    BISO_SVD_DIR_TREE_S *pstNew = NULL;
+    
+    DBGASSERT(NULL != pstRecord);
+    DBGASSERT(NULL != pstPre);
+    DBGASSERT(NULL != pstParent);
+
+    /* 申请内存用于保存文件节点 */
+    pstNew = (BISO_SVD_DIR_TREE_S *)BISO_ZALLOC(sizeof(BISO_SVD_DIR_TREE_S));
+    if (NULL == pstNew)
+    {
+        return NULL;
+    }
+    
+    /* 目录节点属性赋值 */
+    pstNew->uiExtent = pstRecord->uiExtent;
+    pstNew->uiSize = pstRecord->uiSize;
+    
+    /* 节点挂接到当前目录的FileList上 */
+    if (NULL == pstPre)
+    {
+        pstParent->pstFileList = pstNew;
+    }
+    else
+    {
+        pstPre->pstNext = pstNew;
+    }
+    pstNew->pstParent = pstParent;
+
+    return pstNew;
+}
+
+STATIC ULONG BISO_9660_BuildFileList
+(
+    IN  BISO_FILE_S     *pstFile,
+    IN  BISO_PARSER_S   *pstParser,
+    OUT BISO_DIR_TREE_S *pstDirTree
+)
+{
+    UINT uiTail = 0;
+    UINT uiBufSize = 0;
+    UINT uiTotSize = 0;
+    UCHAR *pucBuf = NULL;
+    BISO_DIR_TREE_S *pstPre = NULL;
+    BISO_DIR_TREE_S *pstNew = NULL;
+    BISO_DIR_TREE_S *pstChild = NULL;
+    BISO_DIR_RECORD_S *pstCurrent = NULL;
+    
+    DBGASSERT(NULL != pstFile);
+    DBGASSERT(NULL != pstDirTree);
+
+    /* 读取Directory Record记录 */
+    pucBuf = BISO_9660_ReadDirRecord(pstFile, pstDirTree->uiExtent, &uiBufSize);
+    if (NULL == pucBuf)
+    {
+        return BISO_ERR_ALLOC_MEM;
+    }
+
+    pstCurrent = (BISO_DIR_RECORD_S *)pucBuf;
+    pstChild = pstDirTree->pstChild;
+    
+    while (uiTotSize < uiBufSize)
+    {
+        if (BOOL_TRUE != BISO_DIR_RECORD_IS_PATH(pstCurrent))  /* 只处理文件 */
+        {
+            /* 创建文件节点 */
+            pstNew = BISO_9660_CreateFileNode(pstFile, pstParser, pstCurrent, pstPre, pstDirTree);
+            if (NULL == pstNew)
+            {
+                BISO_FREE(pucBuf);
+                return BISO_ERR_ALLOC_MEM;
+            }
+            pstNew->ui64FileRecordOffset = (UINT64)((UINT64)pstDirTree->uiExtent * BISO_SECTOR_SIZE)
+                + ((ULONG)pstCurrent - (ULONG)pucBuf);
+            pstPre = pstNew;
+        }
+        else
+        {
+            /* 对于子目录在这里更新目录的Rock Ridge扩展信息 */
+            if ((BOOL_TRUE != BISO_9660_IS_CURRENT(pstCurrent)) &&
+                (BOOL_TRUE != BISO_9660_IS_PARENT(pstCurrent))) 
+            {
+                /*
+                 * 这里首先按照Path Table里记录的子目录顺序来判断, 如果是就不用搜索了
+                 * 如果不是则再从子目录列表中查询.
+                 * 这里实际上取决于: 
+                 * Path Table里的子目录记录和Directory Record里面的子目录记录顺序是否一致!!!!
+                 * 绝大多数情况下都是按照字母顺序,两者是一致的, 所以BISO_9660_FindChild一般情况下
+                 * 是不会调用的
+                 */
+                if (pstChild->uiExtent == pstCurrent->uiExtent)
+                {
+                    pstNew = pstChild;
+                    pstChild = pstChild->pstNext;
+                }
+                else
+                {
+                    pstNew = BISO_9660_FindChild(pstDirTree, pstCurrent->uiExtent);
+                }
+            
+                if (NULL != pstNew)
+                {
+                    (VOID)BISO_RRIP_ReadExtInfo(pstFile, pstParser, pstCurrent, pstNew);
+                }
+            }
+        }
+
+        uiTotSize += pstCurrent->ucLength;
+        pstCurrent = (BISO_DIR_RECORD_S *)(pucBuf + uiTotSize);
+        
+        /*
+         * !!!!!!!!!!!!!!!!!!!!!!!!
+         * ISO-9660规定Directory Record记录不能跨逻辑块,所以如果一个逻辑块的最后
+         * 一段区域不够保存一个Directory Record的话这段区域就会废弃(填0)
+         */
+        if (0 == pstCurrent->ucLength)
+        {
+            uiTail = BISO_BLOCK_SIZE - (uiTotSize % BISO_BLOCK_SIZE);
+            uiTotSize += uiTail;
+            pstCurrent = (BISO_DIR_RECORD_S *)((UCHAR *)pstCurrent + uiTail);
+        }
+    }
+
+    BISO_FREE(pucBuf);
+    return BISO_SUCCESS;
+}
+
+/* 通过PathTable构建目录树(只包含目录) 
+这里利用Path Table,因此超过65535个文件夹的ISO文件只能读取前                65535个目录里的内容 */
+STATIC ULONG BISO_9660_BuildPathTree
+(
+    IN    BISO_FILE_S    *pstFile, 
+    INOUT BISO_PARSER_S  *pstParser,
+    OUT   UINT           *puiTotDirNum
+)
+{
+    UINT uiTotDirNum = 0;
+    UINT uiPathTblId = 1;
+    BISO_QUEUE_S *pstQueue = NULL;
+    BISO_DIR_TREE_S *pstNew = NULL;
+    BISO_DIR_TREE_S *pstPre = NULL;
+    BISO_DIR_TREE_S *pstDirTree = NULL;
+    BISO_PATH_TABLE_S *pstPathTable = NULL;
+
+    DBGASSERT(NULL != pstFile);
+    DBGASSERT(NULL != pstParser);
+    DBGASSERT(NULL != puiTotDirNum);
+
+    /*
+     * ISO-9660规定的Path Table的顺序实际上是文件树的一个广度优先遍历的形式
+     * 而处理广度优先遍历时一般需要一个先进先出的队列结构,这里创建一个使用
+     */
+    pstQueue = BISO_QUEUE_Create();
+    if (NULL == pstQueue)
+    {
+        return BISO_ERR_ALLOC_MEM;
+    }
+
+    /* ROOT根目录 */
+    pstPathTable = (BISO_PATH_TABLE_S *)(pstParser->pucPathTable);
+    pstDirTree = &(pstParser->stDirTree);
+    pstDirTree->uiPathTblId = 1;  
+    pstDirTree->uiExtent = pstPathTable->uiExtent;  
+
+    /* 申请统计信息的节点 */
+    pstDirTree->pstDirStat = (BISO_DIR_STAT_S *)BISO_ZALLOC(sizeof(BISO_DIR_STAT_S));
+    if (NULL == pstDirTree->pstDirStat)
+    {
+        return BISO_ERR_ALLOC_MEM;
+    }
+
+    /* 先把ROOT入队列 */
+    BISO_QUEUE_Push(pstQueue, pstDirTree);
+    pstPathTable = (BISO_PATH_TABLE_S *)((UCHAR *)pstPathTable + BISO_9660_PATH_LEN(pstPathTable));
+
+    /* 依次处理队列中的每一项直到队列读空 */
+    while (NULL != (pstDirTree = (BISO_DIR_TREE_S *)BISO_QUEUE_PopHead(pstQueue)))
+    {
+        /* 把该目录下的所有一级子目录读出来入队列 */
+        while ((USHORT)pstDirTree->uiPathTblId == pstPathTable->usParentDirNum)
+        {
+            /* 申请内存用于保存目录节点 */
+            pstNew = (BISO_DIR_TREE_S *)BISO_ZALLOC(sizeof(BISO_DIR_TREE_S));
+            if (NULL == pstNew)
+            {
+                BISO_QUEUE_Destroy(pstQueue);
+                return BISO_ERR_ALLOC_MEM;
+            }
+            
+            /* 目录节点属性赋值 */
+            BISO_UTIL_CopyStr(pstPathTable->szDirName, pstPathTable->ucDirNameLen, pstNew->szName);
+            pstNew->uiExtent = pstPathTable->uiExtent;
+            pstNew->usNameLen = (USHORT)strlen(pstNew->szName);
+            pstNew->uiPathTblId = (++uiPathTblId);
+
+            /* 申请统计信息的节点 */
+            pstNew->pstDirStat = (BISO_DIR_STAT_S *)BISO_ZALLOC(sizeof(BISO_DIR_STAT_S));
+            if (NULL == pstNew->pstDirStat)
+            {
+                BISO_QUEUE_Destroy(pstQueue);
+                return BISO_ERR_ALLOC_MEM;
+            }
+
+            /* 挂接到父目录下 */
+            if (NULL == pstDirTree->pstChild)
+            {
+                pstDirTree->pstChild = pstNew;
+            }
+            else
+            {
+                pstPre->pstNext = pstNew;
+            }
+            pstNew->pstParent = pstDirTree;
+
+            pstPre = pstNew;
+            pstDirTree->pstDirStat->uiCurDirNum++;
+            uiTotDirNum++;
+            BISO_QUEUE_Push(pstQueue, pstNew);
+            pstPathTable = (BISO_PATH_TABLE_S *)((UCHAR *)pstPathTable + BISO_9660_PATH_LEN(pstPathTable));
+        }
+    }
+
+    *puiTotDirNum = uiTotDirNum;
+    BISO_QUEUE_Destroy(pstQueue);
+    return BISO_SUCCESS;
+}
+
+/* 更新整个目录树的目录结构中文件总数、目录总数、总空间大小等信息 */
+ULONG BISO_9660_UpdateTreeStat(INOUT BISO_DIR_TREE_S *pstRoot)
+{
+    VOID *pData = NULL;
+    BISO_QUEUE_S *pstQueue = NULL;
+    BISO_DIR_TREE_S *pstCurDir = NULL;
+    BISO_DIR_STAT_S *pstDirStat = NULL;
+    BISO_DIR_STAT_S *pstPreDirStat = NULL;
+    
+    DBGASSERT(NULL != pstRoot);
+    
+    pstQueue = BISO_QUEUE_Create();
+    if (NULL == pstQueue)
+    {
+        return BISO_ERR_ALLOC_MEM;
+    }
+
+    /* 构建DFS栈 */
+    BISO_9660_FillDfsStack(pstRoot, pstQueue);
+
+    /* 依次弹栈处理 */
+    while (NULL != (pData = BISO_QUEUE_PopTail(pstQueue)))
+    {
+        pstCurDir = (BISO_DIR_TREE_S *)pData;
+        pstDirStat = pstCurDir->pstDirStat;
+
+        /* 更新自己和父节点 */
+        pstDirStat->uiTotDirNum  += pstDirStat->uiCurDirNum;
+        pstDirStat->uiTotFileNum += pstDirStat->uiCurFileNum;
+        pstDirStat->uiTotLinkNum += pstDirStat->uiCurLinkNum;
+        pstDirStat->ui64TotSpace += pstDirStat->ui64CurSpace;
+        pstDirStat->uiTotUsedSec += pstDirStat->uiCurUsedSec;
+
+        if (NULL != pstCurDir->pstParent)  /* ROOT节点没有父节点 */
+        {
+            pstPreDirStat = pstCurDir->pstParent->pstDirStat;
+            pstPreDirStat->uiTotDirNum  += pstDirStat->uiTotDirNum;
+            pstPreDirStat->uiTotFileNum += pstDirStat->uiTotFileNum;
+            pstPreDirStat->uiTotLinkNum += pstDirStat->uiCurLinkNum;
+            pstPreDirStat->ui64TotSpace += pstDirStat->ui64TotSpace;
+            pstPreDirStat->uiTotUsedSec += pstDirStat->uiTotUsedSec;
+        }
+    }
+
+    /* 销毁堆栈 */
+    BISO_QUEUE_Destroy(pstQueue);
+    return BISO_SUCCESS;
+}
+
+
+ULONG BISO_9660_UpdateNodeStat
+(
+    IN BOOL_T bAdd,
+    IN CONST BISO_DIR_TREE_S *pstCurNode,
+    INOUT BISO_DIR_TREE_S *pstParent
+)
+{
+    UINT uiSecNum = 0;
+    BISO_DIR_TREE_S *pstCurDir = NULL;
+    BISO_DIR_STAT_S *pstCurStat = NULL;
+    BISO_DIR_STAT_S *pstPreStat = NULL;
+    BISO_DIR_STAT_S  stExDirStat;
+    
+    DBGASSERT(NULL != pstCurNode);
+
+    memset(&stExDirStat, 0, sizeof(stExDirStat));
+    pstPreStat = pstParent->pstDirStat;
+
+    if (NULL == pstCurNode->pstDirStat)  /* 非目录 */
+    {
+        if (BOOL_TRUE == BISO_DIR_TREE_IS_SYMLINK(pstCurNode))  /* 符号链接 */
+        {
+            /* 更新当前目录链接数统计, 大小为0不用更新 */
+            BISO_STAT_UPDATE(bAdd, pstPreStat->uiCurLinkNum, 1);  
+            stExDirStat.uiTotLinkNum = 1;
+        }
+        else
+        {
+            /* 更新当前目录的文件数,大小等 */
+            uiSecNum = BISO_USED_SECTOR_NUM(pstCurNode->uiSize);
+            BISO_STAT_UPDATE(bAdd, pstPreStat->uiCurFileNum, 1);
+            BISO_STAT_UPDATE(bAdd, pstPreStat->uiCurUsedSec, uiSecNum);
+            BISO_STAT_UPDATE(bAdd, pstPreStat->ui64CurSpace, pstCurNode->uiSize);
+            stExDirStat.uiTotFileNum = 1;
+            stExDirStat.ui64TotSpace = pstCurNode->uiSize;
+            stExDirStat.uiTotUsedSec = uiSecNum;
+        }
+    }
+    else
+    {
+        pstCurStat = pstCurNode->pstDirStat;
+        BISO_STAT_UPDATE(bAdd, pstPreStat->uiCurDirNum,  1);  /* Current只需更新目录数 */
+        BISO_STAT_UPDATE(bAdd, pstPreStat->uiTotDirNum,  pstCurStat->uiTotDirNum);
+        BISO_STAT_UPDATE(bAdd, pstPreStat->uiTotFileNum, pstCurStat->uiTotFileNum);
+        BISO_STAT_UPDATE(bAdd, pstPreStat->uiTotLinkNum, pstCurStat->uiTotLinkNum);
+        BISO_STAT_UPDATE(bAdd, pstPreStat->ui64TotSpace, pstCurStat->ui64TotSpace);
+        BISO_STAT_UPDATE(bAdd, pstPreStat->uiTotUsedSec, pstCurStat->uiTotUsedSec);
+        memcpy(&stExDirStat, pstCurStat, sizeof(stExDirStat)); /* 只会用到Total部分,不用Current部分 */
+    }
+
+    /* 依次更新上层目录的Total部分 */
+    pstCurDir = pstParent->pstParent;
+    while (NULL != pstCurDir)
+    {
+        pstCurStat = pstCurDir->pstDirStat;
+        BISO_STAT_UPDATE(bAdd, pstCurStat->uiTotDirNum,  stExDirStat.uiTotDirNum);
+        BISO_STAT_UPDATE(bAdd, pstCurStat->uiTotFileNum, stExDirStat.uiTotFileNum);
+        BISO_STAT_UPDATE(bAdd, pstCurStat->uiTotLinkNum, stExDirStat.uiTotLinkNum);
+        BISO_STAT_UPDATE(bAdd, pstCurStat->ui64TotSpace, stExDirStat.ui64TotSpace);
+        BISO_STAT_UPDATE(bAdd, pstCurStat->uiTotUsedSec, stExDirStat.uiTotUsedSec);
+        pstCurDir = pstCurDir->pstParent;
+    }
+
+    return BISO_SUCCESS;
+}
+
+ULONG BISO_9660_BuildFileTreeByTable
+(
+    IN  BISO_FILE_S          *pstFile, 
+    OUT BISO_PARSER_S *pstParser
+)
+{   
+    ULONG ulRet;
+    UINT  uiTotDirNum = 0;
+    BISO_QUEUE_S *pstQueue = NULL;
+    BISO_DIR_TREE_S *pstDirTree = NULL;
+
+    DBGASSERT(NULL != pstFile);
+    DBGASSERT(NULL != pstParser);
+
+    /* 先通过Path Table构建目录树(不包含文件) */
+    ulRet = BISO_9660_BuildPathTree(pstFile, pstParser, &uiTotDirNum);
+    if (BISO_SUCCESS != ulRet)
+    {
+        return ulRet;
+    }
+
+    /* 构建每一个目录下的文件列表 */
+
+    /* 创建队列用于递归遍历 */
+    pstQueue = BISO_QUEUE_Create();
+    if (NULL == pstQueue)
+    {
+        return BISO_ERR_ALLOC_MEM;
+    }
+
+    /* ROOT目录入队列 */
+    BISO_QUEUE_Push(pstQueue, &(pstParser->stDirTree));
+
+    /* 循环依次构建 */
+    while (NULL != (pstDirTree = (BISO_DIR_TREE_S *)BISO_QUEUE_PopHead(pstQueue)))
+    {
+        /* 构建文件列表 */
+        ulRet = BISO_9660_BuildFileList(pstFile, pstParser, pstDirTree);
+        if (BISO_SUCCESS != ulRet)
+        {
+            BISO_DIAG("Failed to build file list for dir %s.", pstDirTree->szName);
+            BISO_QUEUE_Destroy(pstQueue);
+            return ulRet;
+        }
+
+        /* 子目录入队列 */
+        if (NULL != pstDirTree->pstChild)
+        {
+            BISO_QUEUE_Push(pstQueue, pstDirTree->pstChild);
+        }
+
+        /* 下一个相邻目录入队列 */
+        if (NULL != pstDirTree->pstNext)
+        {
+            BISO_QUEUE_Push(pstQueue, pstDirTree->pstNext);
+        }
+    }
+    
+    BISO_QUEUE_Destroy(pstQueue);
+    return BISO_SUCCESS;
+}
+
+ULONG BISO_9660_BuildFileTreeRecursively
+(
+    IN  BISO_FILE_S          *pstFile, 
+    OUT BISO_PARSER_S *pstParser
+)
+{
+    UINT uiTail = 0;
+    UINT uiTotSize = 0;
+    UINT uiBufSize = 0;
+    UCHAR *pucBuf = NULL;
+    BISO_QUEUE_S *pstQueue = NULL;
+    BISO_DIR_TREE_S *pstNew = NULL;
+    BISO_DIR_TREE_S *pstPreDir = NULL;
+    BISO_DIR_TREE_S *pstPreFile = NULL;
+    BISO_DIR_TREE_S *pstDirTree = NULL;
+    BISO_DIR_RECORD_S *pstCurrent = NULL;
+
+    DBGASSERT(NULL != pstFile);
+    DBGASSERT(NULL != pstParser);
+
+    /* 先对ROOT进行处理 */
+    pstDirTree = &(pstParser->stDirTree);
+    pstDirTree->uiPathTblId = 1;  
+    pstDirTree->uiExtent = pstParser->pstPVD->stRootDirRecord.uiExtent;  
+
+    /* 申请统计信息的内存 */
+    pstDirTree->pstDirStat = (BISO_DIR_STAT_S *)BISO_ZALLOC(sizeof(BISO_DIR_STAT_S));
+    if (NULL == pstDirTree->pstDirStat)
+    {
+        return BISO_ERR_ALLOC_MEM;
+    }
+
+    /* 创建堆栈,同时ROOT入栈 */
+    pstQueue = BISO_QUEUE_Create();
+    BISO_QUEUE_Push(pstQueue, pstDirTree);
+
+    while (NULL != (pstDirTree = (BISO_DIR_TREE_S *)BISO_QUEUE_PopHead(pstQueue)))
+    {
+        uiTotSize  = 0;
+        pstPreDir  = NULL;
+        pstPreFile = NULL;
+        pstDirTree->uiPathTblId = BISO_UINT_MAX;
+        
+        /* 读取Directory Record记录 */
+        pucBuf = BISO_9660_ReadDirRecord(pstFile, pstDirTree->uiExtent, &uiBufSize);
+        if (NULL == pucBuf)
+        {
+            BISO_QUEUE_Destroy(pstQueue);
+            return BISO_ERR_ALLOC_MEM;
+        }
+
+        pstCurrent = (BISO_DIR_RECORD_S *)pucBuf;
+        
+        while (uiTotSize < uiBufSize)
+        {
+            if (BOOL_TRUE == BISO_DIR_RECORD_IS_PATH(pstCurrent))
+            {
+                if ((BOOL_TRUE != BISO_9660_IS_CURRENT(pstCurrent)) &&
+                    (BOOL_TRUE != BISO_9660_IS_PARENT(pstCurrent))) 
+                {
+                    /* 创建新目录节点 */
+                    pstNew = BISO_9660_CreateDirNode(pstFile, pstParser, pstCurrent, pstPreDir, pstDirTree);
+                    if (NULL == pstNew)
+                    {
+                        BISO_FREE(pucBuf);
+                        BISO_QUEUE_Destroy(pstQueue);
+                        return BISO_ERR_ALLOC_MEM;
+                    }
+                    pstPreDir = pstNew;
+
+                    /* 新目录入栈 */
+                    BISO_QUEUE_Push(pstQueue, pstNew);
+                }
+            }
+            else
+            {
+                pstNew = BISO_9660_CreateFileNode(pstFile, pstParser, pstCurrent, pstPreFile, pstDirTree);
+                if (NULL == pstNew)
+                {
+                    BISO_FREE(pucBuf);
+                    BISO_QUEUE_Destroy(pstQueue);
+                    return BISO_ERR_ALLOC_MEM;
+                }
+                pstNew->ui64FileRecordOffset = (UINT64)((UINT64)pstDirTree->uiExtent * BISO_SECTOR_SIZE)
+                    + ((ULONG)pstCurrent - (ULONG)pucBuf);
+                pstPreFile = pstNew;
+            }
+
+            uiTotSize += pstCurrent->ucLength;
+            pstCurrent = (BISO_DIR_RECORD_S *)(pucBuf + uiTotSize);
+            
+            /*
+             * !!!!!!!!!!!!!!!!!!!!!!!!
+             * ISO-9660规定Directory Record记录不能跨逻辑块,所以如果一个逻辑块的最后
+             * 一段区域不够保存一个Directory Record的话这段区域就会废弃(填0)
+             */
+            if (0 == pstCurrent->ucLength)
+            {
+                uiTail = BISO_BLOCK_SIZE - (uiTotSize % BISO_BLOCK_SIZE);
+                uiTotSize += uiTail;
+                pstCurrent = (BISO_DIR_RECORD_S *)((UCHAR *)pstCurrent + uiTail);
+            }
+        }
+
+        BISO_FREE(pucBuf);
+    }
+
+    BISO_QUEUE_Destroy(pstQueue);
+    return BISO_SUCCESS;
+}
+
+ULONG BISO_9660_BuildSVDFileTreeRecursively
+(
+    IN  BISO_FILE_S          *pstFile, 
+    OUT BISO_PARSER_S *pstParser
+)
+{
+    UINT uiTail = 0;
+    UINT uiTotSize = 0;
+    UINT uiBufSize = 0;
+    UCHAR *pucBuf = NULL;
+    BISO_QUEUE_S *pstQueue = NULL;
+    BISO_SVD_DIR_TREE_S *pstNew = NULL;
+    BISO_SVD_DIR_TREE_S *pstPreDir = NULL;
+    BISO_SVD_DIR_TREE_S *pstPreFile = NULL;
+    BISO_SVD_DIR_TREE_S *pstDirTree = NULL;
+    BISO_DIR_RECORD_S *pstCurrent = NULL;
+
+    DBGASSERT(NULL != pstFile);
+    DBGASSERT(NULL != pstParser);
+
+    /* 先对ROOT进行处理 */
+    pstDirTree = &(pstParser->stSVDDirTree);
+    pstDirTree->uiExtent = pstParser->pstSVD->stRootDirRecord.uiExtent;  
+
+    /* 创建堆栈,同时ROOT入栈 */
+    pstQueue = BISO_QUEUE_Create();
+    BISO_QUEUE_Push(pstQueue, pstDirTree);
+
+    while (NULL != (pstDirTree = (BISO_SVD_DIR_TREE_S *)BISO_QUEUE_PopHead(pstQueue)))
+    {
+        uiTotSize  = 0;
+        pstPreDir  = NULL;
+        pstPreFile = NULL;
+        
+        /* 读取Directory Record记录 */
+        pucBuf = BISO_9660_ReadDirRecord(pstFile, pstDirTree->uiExtent, &uiBufSize);
+        if (NULL == pucBuf)
+        {
+            BISO_QUEUE_Destroy(pstQueue);
+            return BISO_ERR_ALLOC_MEM;
+        }
+
+        pstCurrent = (BISO_DIR_RECORD_S *)pucBuf;
+        
+        while (uiTotSize < uiBufSize)
+        {
+            if (BOOL_TRUE == BISO_DIR_RECORD_IS_PATH(pstCurrent))
+            {
+                if ((BOOL_TRUE != BISO_9660_IS_CURRENT(pstCurrent)) &&
+                    (BOOL_TRUE != BISO_9660_IS_PARENT(pstCurrent))) 
+                {
+                    /* 创建新目录节点 */
+                    pstNew = BISO_9660_CreateSVDDirNode(pstFile, pstParser, pstCurrent, pstPreDir, pstDirTree);
+                    if (NULL == pstNew)
+                    {
+                        BISO_FREE(pucBuf);
+                        BISO_QUEUE_Destroy(pstQueue);
+                        return BISO_ERR_ALLOC_MEM;
+                    }
+                    pstPreDir = pstNew;
+
+                    /* 新目录入栈 */
+                    BISO_QUEUE_Push(pstQueue, pstNew);
+                }
+            }
+            else
+            {
+                pstNew = BISO_9660_CreateSVDFileNode(pstFile, pstParser, pstCurrent, pstPreFile, pstDirTree);
+                if (NULL == pstNew)
+                {
+                    BISO_FREE(pucBuf);
+                    BISO_QUEUE_Destroy(pstQueue);
+                    return BISO_ERR_ALLOC_MEM;
+                }
+                pstNew->ui64FileRecordOffset = (UINT64)((UINT64)pstDirTree->uiExtent * BISO_SECTOR_SIZE)
+                    + ((ULONG)pstCurrent - (ULONG)pucBuf);
+                pstPreFile = pstNew;
+            }
+
+            uiTotSize += pstCurrent->ucLength;
+            pstCurrent = (BISO_DIR_RECORD_S *)(pucBuf + uiTotSize);
+            
+            /*
+             * !!!!!!!!!!!!!!!!!!!!!!!!
+             * ISO-9660规定Directory Record记录不能跨逻辑块,所以如果一个逻辑块的最后
+             * 一段区域不够保存一个Directory Record的话这段区域就会废弃(填0)
+             */
+            if (0 == pstCurrent->ucLength)
+            {
+                uiTail = BISO_BLOCK_SIZE - (uiTotSize % BISO_BLOCK_SIZE);
+                uiTotSize += uiTail;
+                pstCurrent = (BISO_DIR_RECORD_S *)((UCHAR *)pstCurrent + uiTail);
+            }
+        }
+
+        BISO_FREE(pucBuf);
+    }
+
+    BISO_QUEUE_Destroy(pstQueue);
+    return BISO_SUCCESS;
+}
+
+VOID BISO_9660_FreeDirTree(IN BISO_PARSER_S *pstParser)
+{
+    BISO_QUEUE_S *pstQueue = NULL;
+    BISO_DIR_TREE_S *pstRoot = NULL;
+    BISO_DIR_TREE_S *pstCurDir = NULL;
+    BISO_DIR_TREE_S *pstPre = NULL;
+    BISO_DIR_TREE_S *pstNext = NULL;
+
+    if (NULL == pstParser)
+    {
+        return;
+    }
+
+    /* 创建队列 */
+    pstQueue = BISO_QUEUE_Create();
+    if (NULL == pstQueue)
+    {
+        return;
+    }
+
+    pstRoot = &pstParser->stDirTree;
+
+    /* 构建文件树入栈 */
+    BISO_9660_FillDfsStack(pstRoot, pstQueue);
+
+    /* 不需要释放ROOT,把它从队列头弹出来 */
+    BISO_QUEUE_PopHead(pstQueue);
+
+    /* 依次释放各个节点 */
+    while (NULL != (pstCurDir = (BISO_DIR_TREE_S *)BISO_QUEUE_PopTail(pstQueue)))
+    {
+        /* 释放当前目录的文件列表 */
+        for (pstPre = pstCurDir->pstFileList; NULL != pstPre; pstPre = pstNext)
+        {
+            pstNext = pstPre->pstNext;
+            BISO_9600_FREE_DIRTREE(pstPre);
+        }
+
+        /* 释放自己 */
+        BISO_9600_FREE_DIRTREE(pstCurDir);
+    }
+
+    /* 释放ROOT目录的文件列表 */
+    for (pstPre = pstRoot->pstFileList; NULL != pstPre; pstPre = pstNext)
+    {
+        pstNext = pstPre->pstNext;
+        BISO_9600_FREE_DIRTREE(pstPre);
+    }
+
+    /* 释放ROOT自己的扩展信息,但不释放自己本身 */
+    BISO_9600_FREE_STAT(pstRoot);
+    BISO_9600_FREE_POSIX(pstRoot);
+
+    /* 销毁队列 */
+    BISO_QUEUE_Destroy(pstQueue);
+}
+
+VOID BISO_9660_FreeSVDDirTree(IN BISO_PARSER_S *pstParser)
+{
+    BISO_QUEUE_S *pstQueue = NULL;
+    BISO_SVD_DIR_TREE_S *pstRoot = NULL;
+    BISO_SVD_DIR_TREE_S *pstCurDir = NULL;
+    BISO_SVD_DIR_TREE_S *pstPre = NULL;
+    BISO_SVD_DIR_TREE_S *pstNext = NULL;
+
+    if (NULL == pstParser || 
+        0 == pstParser->stSVDDirTree.uiExtent ||
+        0 == pstParser->stSVDDirTree.uiSize)
+    {
+        return;
+    }
+
+    /* 创建队列 */
+    pstQueue = BISO_QUEUE_Create();
+    if (NULL == pstQueue)
+    {
+        return;
+    }
+
+    pstRoot = &pstParser->stSVDDirTree;
+
+    /* 构建文件树入栈 */
+    BISO_9660_FillDfsStack((BISO_DIR_TREE_S *)pstRoot, pstQueue);
+
+    /* 不需要释放ROOT,把它从队列头弹出来 */
+    BISO_QUEUE_PopHead(pstQueue);
+
+    /* 依次释放各个节点 */
+    while (NULL != (pstCurDir = (BISO_SVD_DIR_TREE_S *)BISO_QUEUE_PopTail(pstQueue)))
+    {
+        /* 释放当前目录的文件列表 */
+        for (pstPre = pstCurDir->pstFileList; NULL != pstPre; pstPre = pstNext)
+        {
+            pstNext = pstPre->pstNext;
+            BISO_FREE(pstPre);
+        }
+
+        /* 释放自己 */
+        BISO_FREE(pstCurDir);
+    }
+
+    /* 释放ROOT目录的文件列表 */
+    for (pstPre = pstRoot->pstFileList; NULL != pstPre; pstPre = pstNext)
+    {
+        pstNext = pstPre->pstNext;
+        BISO_FREE(pstPre);
+    }
+
+    /* 销毁队列 */
+    BISO_QUEUE_Destroy(pstQueue);
+}
+
+BISO_PARSER_S * BISO_9660_CreateParser(VOID)
+{
+    BISO_PARSER_S *pstParser = NULL;
+    
+    pstParser = (BISO_PARSER_S *)BISO_ZALLOC(sizeof(BISO_PARSER_S));
+    if (NULL == pstParser)
+    {
+        return NULL;
+    }
+
+    /* 初始化链表 */
+    BISO_DLL_Init(&(pstParser->stVDList));
+
+    return pstParser;
+}
+
+VOID BISO_9660_CleanParser(INOUT BISO_PARSER_S *pstParser)
+{
+    if (NULL == pstParser)
+    {
+        return;
+    }
+
+    /* 释放Volume Descriptor链表 */
+    BISO_DLL_Free(&(pstParser->stVDList));
+
+    /* 释放Path Table */
+    if (NULL != pstParser->pucPathTable)
+    {
+        BISO_FREE(pstParser->pucPathTable);
+    }
+
+    /* 释放 El Torito数据 */
+    if (NULL != pstParser->pucElToritoEntry)
+    {
+        BISO_FREE(pstParser->pucElToritoEntry);
+    }
+
+    /* 释放文件树 */
+    BISO_9660_FreeDirTree(pstParser);
+    BISO_9660_FreeSVDDirTree(pstParser);
+}
+
+VOID BISO_9660_DestroyParser(INOUT BISO_PARSER_S *pstParser)
+{
+    if (NULL == pstParser)
+    {
+        return;
+    }
+
+    /* 清理解析器 */
+    BISO_9660_CleanParser(pstParser);
+    
+    /* 释放解析器自己 */
+    BISO_FREE(pstParser);
+}
+
+ULONG BISO_9660_OpenImage
+(
+    IN BOOL_T bParseSVDDirTree,
+    IN CONST CHAR *pcFileName, 
+    OUT BISO_PARSER_S *pstParser
+)
+{
+    UINT64 ui64FileSize = 0;
+    ULONG ulRet = BISO_SUCCESS;
+    BISO_FILE_S *pstFile = NULL;
+    
+    if ((NULL == pcFileName) || (NULL == pstParser))
+    {
+        return BISO_ERR_NULL_PTR;
+    }
+
+    /* 先看文件大小,过小的文件不可能是ISO文件 */
+    ui64FileSize = BISO_PLAT_GetFileSize(pcFileName);
+    if (ui64FileSize < BISO_SYSTEM_AREA_SIZE + sizeof(BISO_PVD_S))
+    {
+        BISO_DIAG("File len %llu is too small.", (ULONGLONG)ui64FileSize);
+        return BISO_ERR_INVALID_ISO9660;
+    }
+
+    /* 如果已有数据则先清空 */
+    if (NULL != pstParser->pstPVD)
+    {
+        BISO_9660_CleanParser(pstParser);
+    }
+
+    /* 打开ISO文件 */
+    pstFile = BISO_PLAT_OpenExistFile(pcFileName);
+    if (NULL == pstFile)
+    {
+        BISO_DIAG("Failed to open file %s.", pcFileName);
+        return BISO_ERR_OPEN_FILE;
+    }
+
+    scnprintf(pstParser->szFileName, sizeof(pstParser->szFileName), "%s", pcFileName);
+
+    /* 读取Volume Description信息 */
+    ulRet = BISO_9660_ReadVD(pstFile, pstParser);
+    BISO_9660_CHECK_RET(ulRet, pstFile);
+
+    /* 读取Path Table */
+    ulRet = BISO_9660_ReadPathTable(pstFile, pstParser);
+    BISO_9660_CHECK_RET(ulRet, pstFile);
+
+    /* 读取BOOT启动信息 */
+    ulRet = BISO_ELTORITO_ReadBootInfo(pstFile, pstParser);
+    BISO_9660_CHECK_RET(ulRet, pstFile);
+
+    /* 读取Rock Ridge扩展标识 */
+    ulRet = BISO_RRIP_ReadIndicator(pstParser);
+    BISO_9660_CHECK_RET(ulRet, pstFile);
+
+    /* 这里构建文件树有两种方法可供选择, 暂时选择从ROOT递归创建的方法 */
+
+    #if 0
+    /* 从ISO文件中读取整个文件树, 这里选择根据Path Table构建 */
+    ulRet += BISO_9660_BuildFileTreeByTable(pstFile, pstParser);
+    BISO_9660_CHECK_RET(ulRet, pstFile);
+    #else
+    /* 从ISO文件中读取整个文件树, 这里选择从ROOT开始递归构建 */
+    ulRet = BISO_9660_BuildFileTreeRecursively(pstFile, pstParser);
+    BISO_9660_CHECK_RET(ulRet, pstFile);
+    #endif /* #if 0 */
+
+    if (bParseSVDDirTree && pstParser->pstSVD)
+    {
+        ulRet = BISO_9660_BuildSVDFileTreeRecursively(pstFile, pstParser);
+        BISO_9660_CHECK_RET(ulRet, pstFile);
+    }
+
+    /* 更新目录结构里的统计信息 */
+    ulRet = BISO_9660_UpdateTreeStat(&(pstParser->stDirTree));
+    BISO_9660_CHECK_RET(ulRet, pstFile);
+
+    BISO_PLAT_CloseFile(pstFile);
+    return BISO_SUCCESS;
+}
+
+
+ULONG BISO_9660_ParseDate84261
+(
+    IN CONST CHAR *pcDate,
+    OUT BISO_DATE_S *pstDate
+)
+{
+    INT aiBuf[7] = { 0 };
+
+    if ((NULL == pcDate) || (NULL == pstDate))
+    {
+        return BISO_ERR_NULL_PTR;
+    }
+
+    /* 
+     * ECMA-119 8.4.26.1节定义的日期格式,共17个字节 
+     * 前16个字节是字符,第17个字节是有符号整数
+     * 如果前16个字节是字符'0', 最后一个是'\0'则表示时间无效
+     * 形如 "2014122013000500*"
+     */
+
+    if ((0 == pcDate[0]) || (' ' == pcDate[0]) || ('0' == pcDate[0]))
+    {
+        return BISO_ERR_NOT_RECORD;
+    }
+    
+    sscanf(pcDate, "%4d%2d%2d%2d%2d%2d%2d", 
+           aiBuf + 0, aiBuf + 1, aiBuf + 2, 
+           aiBuf + 3, aiBuf + 4, aiBuf + 5, aiBuf + 6);
+    pstDate->usYear    = (USHORT)aiBuf[0];
+    pstDate->ucMonth   = (UCHAR)aiBuf[1];
+    pstDate->ucDay     = (UCHAR)aiBuf[2];
+    pstDate->ucHour    = (UCHAR)aiBuf[3];
+    pstDate->ucMin     = (UCHAR)aiBuf[4];
+    pstDate->ucSecond  = (UCHAR)aiBuf[5];
+    pstDate->usMillSec = (UCHAR)(aiBuf[6] * 10); /* 表示百分之一秒 */
+
+    /* 第17字节表示时区信息, 15分钟为1个单位,4个单位就是1个时区 */
+    pstDate->cZone     = pcDate[16] / 4; 
+    
+    return BISO_SUCCESS;
+}
+
+VOID BISO_9660_FmtDate84261(IN time_t ulTime, IN UINT uiBufSize, OUT CHAR *pcDate)
+{
+    INT iTimeZone = BISO_UTIL_GetTimeZone();
+    struct tm *pstTm = NULL;
+    
+    if (NULL != pcDate)
+    {
+        pstTm = localtime(&ulTime);
+        scnprintf(pcDate, uiBufSize, "%04d%02d%02d%02d%02d%02d%02d",
+                  pstTm->tm_year + 1900,
+                  pstTm->tm_mon + 1,
+                  pstTm->tm_mday,
+                  pstTm->tm_hour,
+                  pstTm->tm_min,
+                  pstTm->tm_sec,
+                  0);
+
+        /* 第17个字节记录当前时区 */
+        pcDate[16] = (CHAR)(4 * iTimeZone);
+    }
+}
+
diff --git a/VtoyTool/BabyISO/biso_9660.h b/VtoyTool/BabyISO/biso_9660.h
new file mode 100644 (file)
index 0000000..9bf33f6
--- /dev/null
@@ -0,0 +1,574 @@
+/******************************************************************************
+ * bios_9660.h
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __BISO_9660_H__
+#define __BISO_9660_H__
+
+#define  BISO_SECTOR_SIZE          2048
+
+#define  BISO_BLOCK_SIZE          2048
+
+#define  BISO_SYSTEM_AREA_SIZE    (16 * BISO_SECTOR_SIZE)
+
+/* Volume Descriptor */
+#define  BISO_VD_TYPE_BOOT   0
+#define  BISO_VD_TYPE_PVD    1  
+#define  BISO_VD_TYPE_SVD    2
+#define  BISO_VD_TYPE_PART   3
+#define  BISO_VD_TYPE_END    255
+
+#define  BISO_VD_ID          "CD001"
+
+#define BISO_USED_SECTOR_NUM(uiFileSize) \
+    (uiFileSize / BISO_SECTOR_SIZE) + ((uiFileSize % BISO_SECTOR_SIZE > 0) ? 1 : 0)
+
+#define BISO_9660_IS_CURRENT(pstDirRecord) \
+    (((1 == pstDirRecord->ucNameLen) && (0x0 == pstDirRecord->szName[0])) ? BOOL_TRUE : BOOL_FALSE)
+
+#define BISO_9660_IS_PARENT(pstDirRecord) \
+    (((1 == pstDirRecord->ucNameLen) && (0x1 == pstDirRecord->szName[0])) ? BOOL_TRUE : BOOL_FALSE)
+
+#define BISO_9600_FREE_STAT(pstDir) \
+{ \
+    if (NULL != pstDir->pstDirStat) \
+    { \
+        BISO_FREE(pstDir->pstDirStat); \
+    }\
+}
+
+#define BISO_9600_FREE_POSIX(pstDir) \
+{ \
+    if (NULL != pstDir->pstPosixInfo) \
+    { \
+        if (NULL != pstDir->pstPosixInfo->pcLinkSrc) \
+        { \
+            BISO_FREE(pstDir->pstPosixInfo->pcLinkSrc); \
+        } \
+        BISO_FREE(pstDir->pstPosixInfo); \
+    } \
+}
+
+#define BISO_9600_FREE_DIRTREE(pstDir) \
+{ \
+    BISO_9600_FREE_STAT(pstDir); \
+    BISO_9600_FREE_POSIX(pstDir); \
+    BISO_FREE(pstDir); \
+}
+
+#if (__BYTE_ORDER == __LITTLE_ENDIAN)
+
+#define  BISO_DEF_723(usData) \
+    UINT16 usData;\
+    UINT16 usData##Swap;
+
+#define  BISO_DEF_733(uiData) \
+    UINT32 uiData;\
+    UINT32 uiData##Swap;
+
+#define  BISO_PATHTBL_LOCATION(pstPVD)  (pstPVD->uiTypeLPathTblLoc)
+
+#elif (__BYTE_ORDER == __BIG_ENDIAN)
+
+#define  BISO_DEF_723(usData) \
+    UINT16 usData##Swap;\
+    UINT16 usData;
+
+#define  BISO_DEF_733(uiData) \
+    UINT32 uiData##Swap;\
+    UINT32 uiData;
+
+#define  BISO_PATHTBL_LOCATION(pstPVD)  (pstPVD->uiTypeMPathTblLoc)
+
+#else
+#error ("you must first define __BYTE_ORDER !!!")
+#endif
+
+#pragma pack(1)
+
+typedef struct tagBISO_DATE_915
+{
+    UCHAR ucYear;      /* 1900+ */
+    UCHAR ucMonth;    
+    UCHAR ucDay;      
+    UCHAR ucHour;     
+    UCHAR ucMin;      
+    UCHAR ucSec;      
+    CHAR  cTimeZone;   
+}BISO_DATE_915_S;
+
+typedef struct tagBISO_PATH_TABLE
+{
+    UCHAR ucDirNameLen;
+
+    UCHAR ucExtAttrRecordLen;
+
+    UINT32  uiExtent;
+
+    UINT16 usParentDirNum;
+
+    CHAR  szDirName[1];
+       
+    /* UCHAR ucPadding; */
+}BISO_PATH_TABLE_S;
+
+#define BISO_9660_PATH_LEN(pstPath) (pstPath->ucDirNameLen + 8 + (pstPath->ucDirNameLen & 0x01))
+
+typedef struct tagBISO_DIR_RECORD
+{
+    UCHAR ucLength;
+
+    UCHAR ucExtAttrLen;
+
+    BISO_DEF_733(uiExtent)
+
+    BISO_DEF_733(uiSize)
+
+    BISO_DATE_915_S stDate;    
+
+    UCHAR ucFlags;
+
+    UCHAR ucFileUnitSize;
+
+    UCHAR ucInterLeave;
+
+    BISO_DEF_723(uiVolumeSeqNum)
+
+    UCHAR ucNameLen;
+
+    CHAR  szName[1];
+
+    /* UCHAR aucSystemUse[xxxx] */
+}BISO_DIR_RECORD_S;
+
+#define BISO_DIR_RECORD_IS_PATH(pstDirRecord) \
+    ((1 == ((pstDirRecord->ucFlags >> 1) & 0x01)) ? BOOL_TRUE : BOOL_FALSE)
+
+typedef struct tagBISO_VOLUME_DESC 
+{
+    UCHAR ucType;
+
+    CHAR  szId[5];
+
+    UCHAR ucVersion;
+
+    UCHAR ucData[2041];
+}BISO_VD_S;
+
+typedef struct tagBISO_PRIMARY_VOLUME_DESC
+{
+    UCHAR ucType;
+
+    CHAR  szId[5];
+
+    /* ucVersion: version [ISODCL (  7,   7)] */
+    UCHAR ucVersion;
+
+    /* ucResv1: unused1 [ISODCL (  8,   8)] */
+    UCHAR ucResv1;
+
+    /* 
+     * szSystemId[32]: system_id [ISODCL (  9,  40)] 
+     */
+    CHAR  szSystemId[32];
+
+    /* szVolumeId[32]: volume_id [ISODCL ( 41,  72)] */
+    CHAR  szVolumeId[32];
+
+    /* aucResv2[8]: unused2 [ISODCL ( 73,  80)] */
+    UCHAR aucResv2[8];
+
+    BISO_DEF_733(uiVolumeSpace)
+
+    /* aucResv3[32]: unused3 [ISODCL ( 89,  120)] */
+    UCHAR aucResv3[32];
+
+    BISO_DEF_723(usVolumeSet)
+
+    /* 
+     * usVolumeSeq: volume_sequence_number [ISODCL (125, 128)] 
+     */
+    BISO_DEF_723(usVolumeSeq)
+    
+    /* 
+     * usBlockSize: logical_block_size [ISODCL (129, 132)]
+     */
+    BISO_DEF_723(usBlockSize)
+
+    /*
+     * uiPathTblSize: path_table_size [ISODCL (133, 140)] 
+     */
+    BISO_DEF_733(uiPathTblSize)
+
+    UINT32 uiTypeLPathTblLoc;
+
+    UINT32 uiOptTypeLPathTblLoc;
+
+    UINT32 uiTypeMPathTblLoc;
+    
+    UINT32 uiOptTypeMPathTblLoc;
+
+    BISO_DIR_RECORD_S stRootDirRecord;
+
+    /* szVolumeSetId: volume_set_id [ISODCL (191, 318)]  */
+    CHAR szVolumeSetId[128];
+    
+    /* szPublisherId: publisher_id [ISODCL (319, 446)]  */
+    CHAR szPublisherId[128];
+    
+    /* szPreparerId: preparer_id [ISODCL (447, 574)]  */
+    CHAR szPreparerId[128];
+    
+    /* szApplicationId: application_id [ISODCL (575, 702)]  */
+    CHAR szApplicationId[128];
+    
+    /* szCopyrightFileId: copyright_file_id [ISODCL (703, 739)] [1] */
+    CHAR szCopyrightFileId[37];
+    
+    /* szAbstractFileId: abstract_file_id [ISODCL (740, 776)] */
+    CHAR szAbstractFileId[37];
+    
+    /* szBibliographicFileId: bibliographic_file_id [ISODCL (777, 813)]  */
+    CHAR szBibliographicFileId[37];
+
+    /* szCreationDate: creation_date [ISODCL (814, 830)]  */
+    CHAR szCreationDate[17];
+    
+    /* szModifyDate: modification_date [ISODCL (831, 847)] */
+    CHAR szModifyDate[17];
+    
+    /* szExpirationDate: expiration_date [ISODCL (848, 864)]  */
+    CHAR szExpirationDate[17];
+    
+    /* szEffectiveDate: effective_date [ISODCL (865, 881)]  */
+    CHAR szEffectiveDate[17];
+    
+    /* ucFileStructVer: file_structure_version [ISODCL (882, 882)]  */
+    UCHAR ucFileStructVer;
+    
+    /* ucResv4: unused4 [ISODCL (883, 883)] */
+    UCHAR ucResv4;
+    
+    /* aucApplicationData: application_data [ISODCL (884, 1395)] */
+    UCHAR aucApplicationData[512];
+
+    /* ucResv5: unused5 [ISODCL (1396, 2048)] */
+    UCHAR aucResv5[653];
+}BISO_PVD_S;
+
+typedef struct tagBISO_SUPPLEMENTARY_VOLUME_DESC
+{
+    UCHAR ucType;
+
+    /* 
+     * szId[5]: id [ISODCL (  2,   6)] 
+     */
+    CHAR  szId[5];
+
+    /* ucVersion: version [ISODCL (  7,   7)] */
+    UCHAR ucVersion;
+
+    /* 
+     * ucFlags: flags [ISODCL (  8,   8)] 
+     */
+    UCHAR ucFlags;
+
+    /* 
+     * szSystemId[32]: system_id [ISODCL (  9,  40)] 
+     */
+    CHAR  szSystemId[32];
+
+    /* szVolumeId[32]: volume_id [ISODCL ( 41,  72)] 名称, 注意[1] */
+    CHAR  szVolumeId[32];
+
+    /* aucResv2[8]: unused2 [ISODCL ( 73,  80)] */
+    UCHAR aucResv2[8];
+
+    /* 
+     * uiVolumeSpace: volume_space_size [ISODCL ( 81,  88)] 
+     */
+    BISO_DEF_733(uiVolumeSpace)
+
+    /* aucEscape[32]: escape [ISODCL ( 89,  120)] */
+    UCHAR aucEscape[32];
+
+    /*
+     * usVolumeSet: volume_set_size [ISODCL (121, 124)] 
+     */
+    BISO_DEF_723(usVolumeSet)
+
+    /* 
+     * usVolumeSeq: volume_sequence_number [ISODCL (125, 128)] 
+     */
+    BISO_DEF_723(usVolumeSeq)
+    
+    /* 
+     * usBlockSize: logical_block_size [ISODCL (129, 132)]
+     */
+    BISO_DEF_723(usBlockSize)
+
+    BISO_DEF_733(uiPathTblSize)
+
+    UINT32 uiTypeLPathTblLoc;
+
+    UINT32 uiOptTypeLPathTblLoc;
+
+    UINT32 uiTypeMPathTblLoc;
+    
+    UINT32 uiOptTypeMPathTblLoc;
+
+    BISO_DIR_RECORD_S stRootDirRecord;
+
+    /* szVolumeSetId: volume_set_id [ISODCL (191, 318)]  */
+    CHAR szVolumeSetId[128];
+    
+    /* szPublisherId: publisher_id [ISODCL (319, 446)]  */
+    CHAR szPublisherId[128];
+    
+    /* szPreparerId: preparer_id [ISODCL (447, 574)] */
+    CHAR szPreparerId[128];
+    
+    /* szApplicationId: application_id [ISODCL (575, 702)] */
+    CHAR szApplicationId[128];
+    
+    /* szCopyrightFileId: copyright_file_id [ISODCL (703, 739)] */
+    CHAR szCopyrightFileId[37];
+    
+    /* szAbstractFileId: abstract_file_id [ISODCL (740, 776)]*/
+    CHAR szAbstractFileId[37];
+    
+    /* szBibliographicFileId: bibliographic_file_id [ISODCL (777, 813)] */
+    CHAR szBibliographicFileId[37];
+
+    /* szCreationDate: creation_date [ISODCL (814, 830)]*/
+    CHAR szCreationDate[17];
+    
+    /* szModifyDate: modification_date [ISODCL (831, 847)] */
+    CHAR szModifyDate[17];
+    
+    /* szExpirationDate: expiration_date [ISODCL (848, 864)]  */
+    CHAR szExpirationDate[17];
+    
+    /* szEffectiveDate: effective_date [ISODCL (865, 881)]  */
+    CHAR szEffectiveDate[17];
+    
+    /* ucFileStructVer: file_structure_version [ISODCL (882, 882)]  */
+    UCHAR ucFileStructVer;
+    
+    /* ucResv4: unused4 [ISODCL (883, 883)] */
+    UCHAR ucResv4;
+    
+    /* aucApplicationData: application_data [ISODCL (884, 1395)] */
+    UCHAR aucApplicationData[512];
+
+    /* ucResv5: unused5 [ISODCL (1396, 2048)] */
+    UCHAR aucResv5[653];
+}BISO_SVD_S;
+
+typedef struct tagBISO_BOOT_DESC
+{
+    UCHAR ucType;
+    /* 
+     * szId[5]: id [ISODCL (  2,   6)] 
+     */
+    CHAR szId[5];
+
+    /* ucVersion: version [ISODCL (  7,   7)] */
+    UCHAR ucVersion;
+
+    CHAR szBootSystemId[32];
+
+    UCHAR aucResv[32];
+
+    UINT32 uiBootCatlogStart;
+
+    UCHAR aucBootData[1972];
+}BISO_BVD_S;
+
+typedef struct tagBISO_EXTATTR_RECORD
+{
+    BISO_DEF_723(usOwnerId)
+    BISO_DEF_723(usGroupId)
+
+    USHORT usPermission;
+
+    CHAR szFileCreateDate[17];
+    CHAR szFileModifyDate[17];
+    CHAR szFileExpirationDate[17];
+    CHAR szFileEffectiveDate[17];
+
+    UCHAR ucRecordFormat;
+
+    UCHAR ucRecordAttr;
+
+    BISO_DEF_723(usRecordLen)
+
+    CHAR szSystemId[32];
+
+    UCHAR aucSystemData[64];
+
+    UCHAR ucRecordVersion;
+
+    UCHAR ucEscapeLen;
+
+    UCHAR aucResv[64];
+
+    BISO_DEF_723(usAppDataLen)
+    
+    /* UCHAR aucAppData[xxx]; */
+    /* UCHAR aucEscape[xxx]; */
+}BISO_EXTATTR_RECORD_S;
+
+#pragma pack()
+
+typedef struct tagBISO_VD_NODE
+{
+    BISO_DLL_NODE_S stNode;
+    BISO_VD_S stVD;
+}BISO_VD_NODE_S;
+
+typedef struct tagBISO_POSIX_INFO
+{
+    BOOL_T bHasNMEntry;
+    UINT uiPosixFileMode;
+    UINT uiPosixFileLink;
+    UINT uiPosixFileUserId;
+    UINT uiPosixFileGroupId;
+    UINT uiPosixFileSNO;
+    UINT64 ui64DevNum;
+    BISO_DATE_S stCreateTime;
+    BISO_DATE_S stModifyTime;
+    BISO_DATE_S stLastAccessTime;
+    BISO_DATE_S stLastAttrChangeTime;
+    BISO_DATE_S stLastBackupTime;
+    BISO_DATE_S stExpirationTime;
+    BISO_DATE_S stEffectiveTime;
+
+    UINT  uiLinkLen;
+    CHAR *pcLinkSrc;
+}BISO_POSIX_INFO_S;
+
+typedef struct tagBISO_SVD_DIR_TREE
+{
+    struct tagBISO_SVD_DIR_TREE *pstParent;
+    struct tagBISO_SVD_DIR_TREE *pstNext;
+    struct tagBISO_SVD_DIR_TREE *pstChild;
+
+    UINT uiExtent;
+    UINT uiSize;
+
+    UINT64 ui64FileRecordOffset;
+    struct tagBISO_SVD_DIR_TREE *pstFileList;
+}BISO_SVD_DIR_TREE_S;
+
+typedef struct tagBISO_DIR_TREE
+{
+    struct tagBISO_DIR_TREE *pstParent;
+    struct tagBISO_DIR_TREE *pstNext;
+    struct tagBISO_DIR_TREE *pstChild;
+
+    USHORT usNameLen;
+    CHAR szName[300];
+    UINT uiPathTblId;
+    UINT uiExtent;
+    UINT uiSize;
+
+    UINT64 ui64FileRecordOffset;
+
+    BISO_DIR_STAT_S *pstDirStat;
+
+    BISO_POSIX_INFO_S *pstPosixInfo;
+
+    struct tagBISO_DIR_TREE *pstFileList;
+}BISO_DIR_TREE_S;
+
+#define BISO_DIR_TREE_IS_SYMLINK(pstDirTree) \
+    (((NULL != pstDirTree->pstPosixInfo) && \
+      (NULL != pstDirTree->pstPosixInfo->pcLinkSrc)) ? BOOL_TRUE : BOOL_FALSE)
+
+typedef struct tagBISO_PARSER
+{
+    CHAR szFileName[1024];
+
+    BISO_PVD_S *pstPVD;
+    BISO_SVD_S *pstSVD;
+    BISO_BVD_S *pstBVD;
+    BISO_DLL_S stVDList;
+
+    UCHAR *pucPathTable;
+
+    UINT uiElToritoLen;
+    UCHAR *pucElToritoEntry;
+
+    UCHAR ucRRIPVersion;
+    UCHAR ucRRIPSkipLen;
+
+    BISO_DIR_TREE_S stDirTree;
+    
+    BISO_SVD_DIR_TREE_S stSVDDirTree;
+}BISO_PARSER_S;
+
+ULONG BISO_9660_OpenImage
+(
+    IN BOOL_T bParseSVDDirTree,
+    IN CONST CHAR *pcFileName, 
+    OUT BISO_PARSER_S *pstParser
+);
+BISO_PARSER_S * BISO_9660_CreateParser(VOID);
+VOID BISO_9660_DestroyParser(INOUT BISO_PARSER_S *pstParser);
+ULONG BISO_9660_ParseDate84261
+(
+    IN CONST CHAR *pcDate,
+    OUT BISO_DATE_S *pstDate
+);
+VOID BISO_9660_FmtDate84261(IN time_t ulTime, IN UINT uiBufSize, OUT CHAR *pcDate);
+VOID BISO_9660_FillDfsStack
+(
+    IN BISO_DIR_TREE_S *pstTop, 
+    INOUT BISO_QUEUE_S *pstQueue
+);
+ULONG BISO_9660_ExtractFile
+(
+    IN BISO_FILE_S *pstFile, 
+    IN CONST CHAR  *pcDstFullPath,
+    IN CONST BISO_DIR_TREE_S *pstFileNode,
+    IN CONST BISO_EXTRACT_CTRL_S *pstCtrl
+);
+
+/* 拼接路径 */
+#define BISO_EXTRACE_CAT_PATH(szFullPath, uiCurPos, szName, NameLen) \
+{\
+    memcpy(szFullPath + uiCurPos, szName, NameLen); \
+    uiCurPos += NameLen; \
+    szFullPath[uiCurPos++] = '/'; \
+    szFullPath[uiCurPos] = 0; \
+}
+
+/* 检查结果返回 */
+#define BISO_9660_CHECK_RET(ulRet, pstFile) \
+    if (BISO_SUCCESS != ulRet) \
+    { \
+        BISO_PLAT_CloseFile(pstFile); \
+        return ulRet; \
+    }
+
+BISO_DIR_TREE_S * BISO_UTIL_FindLinkTgt(IN BISO_DIR_TREE_S *pstCurNode);
+
+#endif /* __BISO_9660_H__ */
+
diff --git a/VtoyTool/BabyISO/biso_dump.c b/VtoyTool/BabyISO/biso_dump.c
new file mode 100644 (file)
index 0000000..695ccde
--- /dev/null
@@ -0,0 +1,87 @@
+/******************************************************************************
+ * biso_dump.c
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "biso.h"
+#include "biso_list.h"
+#include "biso_util.h"
+#include "biso_9660.h"
+#include "biso_eltorito.h"
+#include "biso_rockridge.h"
+#include "biso_joliet.h"
+#include "biso_dump.h"
+
+
+VOID BISO_DUMP_ShowFileTree
+(
+    IN UINT uiDepth,
+    IN CONST BISO_DIR_TREE_S *pstDirTree
+)
+{
+    UINT i;
+    
+    if (NULL == pstDirTree)
+    {
+        return;
+    }
+
+    for (i = 0; i + 1 < uiDepth; i++)
+    {
+        BISO_DUMP("    ");
+    }
+
+    if (BOOL_TRUE == BISO_DIR_TREE_IS_SYMLINK(pstDirTree))
+    {
+        BISO_DUMP("|-- %s --> %s", pstDirTree->szName, pstDirTree->pstPosixInfo->pcLinkSrc); 
+    }
+    else
+    {
+        BISO_DUMP("|-- %s", pstDirTree->szName);
+    }
+
+    BISO_DUMP(" %u %u\n", pstDirTree->uiExtent, pstDirTree->uiSize);
+    
+    
+    #if 0
+    if (NULL != pstDirTree->pstDirStat)
+    {
+        BISO_DUMP(" ([%u %u %u]  [%u %u %u]\n", 
+            pstDirTree->pstDirStat->uiCurDirNum, 
+            pstDirTree->pstDirStat->uiCurFileNum, 
+            pstDirTree->pstDirStat->uiCurLinkNum, 
+            pstDirTree->pstDirStat->uiTotDirNum, 
+            pstDirTree->pstDirStat->uiTotFileNum,
+            pstDirTree->pstDirStat->uiTotLinkNum); 
+    }
+    else
+    {
+        BISO_DUMP("\n");
+    }
+    #endif /* #if 0 */
+
+    /* 递归显示子目录 */
+    BISO_DUMP_ShowFileTree(uiDepth + 1, pstDirTree->pstChild);
+
+    /* 显示本目录内的文件列表 */
+    BISO_DUMP_ShowFileTree(uiDepth + 1, pstDirTree->pstFileList);
+
+    /* 显示下一个同级目录 */
+    BISO_DUMP_ShowFileTree(uiDepth, pstDirTree->pstNext);
+}
+
diff --git a/VtoyTool/BabyISO/biso_dump.h b/VtoyTool/BabyISO/biso_dump.h
new file mode 100644 (file)
index 0000000..e1d67fa
--- /dev/null
@@ -0,0 +1,83 @@
+/******************************************************************************
+ * biso_dump.h
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __BISO_DUMP_H__
+#define __BISO_DUMP_H__
+
+#define BISO_DUMP_INT(Name, Value)  BISO_DUMP("%-24s : %u\n", Name, Value)
+#define BISO_DUMP_STR(Name, Value, szBuf) \
+    BISO_DUMP("%-24s : %s\n", Name, BISO_UTIL_CopyStr(Value, sizeof(Value), szBuf))
+
+#define BISO_DUMP_CHAR(str, len) \
+{\
+    UINT uiLoop;\
+    for (uiLoop = 0; uiLoop < (UINT)len; uiLoop++)\
+    {\
+        BISO_DUMP("%c", str[uiLoop]);\
+    }\
+    BISO_DUMP("\n");\
+}
+
+
+#define BISO_DUMP_BYTE(Buf, Len) \
+{ \
+    UINT i; \
+    for (i = 0; i < Len; i++) \
+    { \
+        BISO_DUMP("%02x ", Buf[i]); \
+    } \
+    BISO_DUMP("\n"); \
+}
+
+/* 显示日期 */
+#define BISO_DUMP_DAY(Name, Value) \
+{\
+    ULONG _ulRet;\
+    BISO_DATE_S _stDate;\
+    _ulRet = BISO_9660_ParseDate84261((Value), &_stDate);\
+    if (BISO_SUCCESS == _ulRet)\
+    {\
+        BISO_DUMP("%-24s : %04u-%02u-%02u %02u:%02u:%02u.%03u ",\
+                  (Name), _stDate.usYear, _stDate.ucMonth, _stDate.ucDay,\
+                  _stDate.ucHour, _stDate.ucMin, _stDate.ucSecond,\
+                  _stDate.usMillSec);\
+        if (_stDate.cZone > 0)\
+        {\
+            BISO_DUMP("GMT+%d\n", _stDate.cZone);\
+        }\
+        else\
+        {\
+            BISO_DUMP("GMT%d\n", _stDate.cZone);\
+        }\
+    }\
+    else\
+    {\
+        BISO_DUMP("%-24s : ---\n", (Name));\
+    }\
+}
+
+VOID BISO_DUMP_ShowFileTree
+(
+    IN UINT uiDepth,
+    IN CONST BISO_DIR_TREE_S *pstDirTree
+);
+
+#endif /* __BISO_DUMP_H__ */
+
diff --git a/VtoyTool/BabyISO/biso_eltorito.c b/VtoyTool/BabyISO/biso_eltorito.c
new file mode 100644 (file)
index 0000000..173c505
--- /dev/null
@@ -0,0 +1,161 @@
+/******************************************************************************
+ * bios_eltorito.c
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include "biso.h"
+#include "biso_list.h"
+#include "biso_util.h"
+#include "biso_9660.h"
+#include "biso_eltorito.h"
+
+ULONG BISO_ELTORITO_ReadBootInfo
+(
+    IN  BISO_FILE_S   *pstFile, 
+    OUT BISO_PARSER_S *pstParser
+)
+{
+    USHORT i;
+    UINT uiReadLen = 0;
+    UINT64 ui64Seek = 0;
+    UCHAR aucBuf[BISO_SECTOR_SIZE];
+    BISO_MBUF_S stMBuf;
+    BISO_TORITO_SECHDR_ENTRY_S *pstSecHdr = NULL;
+    BISO_TORITO_SECTION_ENTRY_S *pstSection = NULL;
+    
+    DBGASSERT(NULL != pstFile);
+    DBGASSERT(NULL != pstParser);
+
+    if (NULL == pstParser->pstBVD)
+    {
+        return BISO_SUCCESS;
+    }
+
+    memset(&stMBuf, 0, sizeof(stMBuf));
+    ui64Seek = (UINT64)pstParser->pstBVD->uiBootCatlogStart * BISO_SECTOR_SIZE;
+    BISO_PLAT_SeekFile(pstFile, ui64Seek, SEEK_SET);
+
+    /* 先读取1个逻辑扇区的内容 */
+    uiReadLen = (UINT)BISO_PLAT_ReadFile(pstFile, 1, BISO_SECTOR_SIZE, aucBuf);
+    if (uiReadLen != BISO_SECTOR_SIZE)
+    {
+        BISO_DIAG("Read len %u buf len %u.", uiReadLen, BISO_SECTOR_SIZE);
+        return BISO_ERR_READ_FILE;
+    }
+
+    /* 前两条Entry固定是Validation和Initial */
+    pstSecHdr = (BISO_TORITO_SECHDR_ENTRY_S *)(aucBuf + 2 * BISO_ELTORITO_ENTRY_LEN);
+    pstSection = (BISO_TORITO_SECTION_ENTRY_S *)pstSecHdr;
+
+    while ((0x90 == pstSecHdr->ucFlag) || (0x91 == pstSecHdr->ucFlag))
+    {
+        pstSecHdr = (BISO_TORITO_SECHDR_ENTRY_S *)pstSection;
+        BISO_ELTORITO_ENTRY_STEP(pstSection, pstFile, aucBuf, stMBuf);
+
+        for (i = 0; i < pstSecHdr->usSecEntryNum; )
+        {
+            /* 
+             * Section Entry和Extension Entry都是由ucFlag的Bit5决定是否结束 
+             * 因此这里全部都用Section Entry的结构做判断
+             */
+            if (0 == (pstSection->ucFlag & 0x10))
+            {
+                i++;
+            }
+           
+            BISO_ELTORITO_ENTRY_STEP(pstSection, pstFile, aucBuf, stMBuf);            
+        }
+    
+        if (0x91 == pstSecHdr->ucFlag) /* 91代表最后一个 */
+        {
+            break;
+        }
+    }
+
+    if ((UCHAR *)pstSection > aucBuf)
+    {
+        (VOID)BISO_MBUF_Append(&stMBuf, (UCHAR *)pstSection - aucBuf, aucBuf);
+    }
+
+    /* 保存到全局结构中 */
+    pstParser->uiElToritoLen = stMBuf.uiTotDataSize;    
+    pstParser->pucElToritoEntry = (UCHAR *)BISO_MALLOC(pstParser->uiElToritoLen);
+    if (NULL == pstParser->pucElToritoEntry)
+    {
+        BISO_MBUF_Free(&stMBuf);
+        return BISO_ERR_ALLOC_MEM;
+    }
+
+    BISO_MBUF_CopyToBuf(&stMBuf, pstParser->pucElToritoEntry);
+    BISO_MBUF_Free(&stMBuf);
+    return  BISO_SUCCESS;
+}
+
+VOID BISO_ELTORITO_Dump(IN CONST BISO_PARSER_S *pstParser)
+{   
+    BISO_DUMP("uiElToritoLen=%u\n", pstParser->uiElToritoLen);
+}
+
+
+UINT BISO_ELTORITO_GetBootEntryNum(IN CONST BISO_PARSER_S *pstParser)
+{
+    UINT uiRet = 0;
+    UINT uiEntryNum = 0;
+    UCHAR *pucData = NULL;
+    BISO_TORITO_VALIDATION_ENTRY_S *pstValidation = NULL;
+    BISO_TORITO_INITIAL_ENTRY_S *pstInitial = NULL;
+    
+    if (NULL == pstParser->pucElToritoEntry)
+    {
+        return 0;
+    }
+
+    uiEntryNum = pstParser->uiElToritoLen / BISO_ELTORITO_ENTRY_LEN;
+    pstValidation = (BISO_TORITO_VALIDATION_ENTRY_S *)pstParser->pucElToritoEntry;
+    pstInitial = (BISO_TORITO_INITIAL_ENTRY_S *)(pstValidation + 1);
+
+    if (pstInitial->ucBootId == 0x88)
+    {
+        uiRet++;
+    }
+
+    pucData = pstParser->pucElToritoEntry + 2 * BISO_ELTORITO_ENTRY_LEN;
+    uiEntryNum-= 2;
+
+    while (uiEntryNum > 0)
+    {
+        if ((0x90 == pucData[0]) || (0x91 == pucData[0]))
+        {
+        }
+        else if (0x44 == pucData[0])
+        {
+        }
+        else
+        {
+            if (((BISO_TORITO_SECTION_ENTRY_S *)pucData)->ucBootId == 0x88)
+            {
+                uiRet++;
+            }
+        }
+        pucData += BISO_ELTORITO_ENTRY_LEN;
+        uiEntryNum--;
+    }
+
+    return uiRet;
+}
+
diff --git a/VtoyTool/BabyISO/biso_eltorito.h b/VtoyTool/BabyISO/biso_eltorito.h
new file mode 100644 (file)
index 0000000..6afa987
--- /dev/null
@@ -0,0 +1,246 @@
+/******************************************************************************
+ * bios_eltorito.h
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+/*
+ * EL TORITO扩展规范相关定义
+ * 注意点:
+ *   [1] EL TORITO扩展规范里定义的数据都是小字节序的 
+ */
+
+#ifndef __BISO_ELTORITO_H__
+#define __BISO_ELTORITO_H__
+
+/* 
+ * EL TORITO规范里对于Boot Record里面BootCatlog指向的扩展区域做了
+ * 结构定义,分成一条条的表项,每一条表项固定32个字节长,1个扇区
+ * 可以保存64条表项。BootCatlog可以占用多个扇区。
+ * 表项必须按照如下顺序保存:
+ *   ID   Entry
+ *    0   Validation Entry
+ *    1   Initial Entry
+ *    2   Section Header
+ *    3   Section Entry
+ *    4   Section Extension Entry 1
+ *    5   Section Extension Entry 2
+ *    6   Section Extension Entry 3
+ *        ....
+ *    N   Section Header
+ *    N+1 Section Entry
+ *    N+2 Section Extension Entry 1
+ *    N+3 Section Extension Entry 2
+ *    N+4 Section Extension Entry 3
+ *        ....
+ */
+
+#define  BISO_ELTORITO_ENTRY_LEN    32
+
+#pragma pack(1)
+
+/* 检验表项, 必须是第一条 */
+typedef struct tagBISO_TORITO_VALIDATION_ENTRY
+{
+    UCHAR ucHeaderID;  /* Must be 01 */
+
+    /* 
+     * PlatID: CPU平台架构
+     * 0x00: x86
+     * 0x01: PowerPC
+     * 0x02: Mac
+     * 0xEF: EFI System Partition
+     */
+    UCHAR ucPlatID;
+
+    UINT16 usResv;
+
+    /* ID,一般用来保存CD-ROM的制造商信息 */
+    CHAR szID[24];
+
+    /* 
+     * 校验补充字段, 注意[1]
+     * 这个字段用来保证整个Validation Entry的数据
+     * 按照WORD(双字节)累加起来为0(截取之后) 
+     */
+    UINT16 usCheckSum;
+
+    /* 魔数字,检验使用,值必须为0x55和0xAA */
+    UCHAR ucData55;
+    UCHAR ucDataAA;
+}BISO_TORITO_VALIDATION_ENTRY_S;
+
+/* 默认初始化表项(BIOS里面的 INT 13) */
+typedef struct tagBISO_TORITO_INITIAL_ENTRY
+{
+    /* BOOTID: 0x88:Bootable, 00:Not Bootable */
+    UCHAR ucBootId;
+
+    /* 
+     * ucBootMedia:
+     * Bit0 - Bit3的值:
+     *  0: No Emulation
+     *  1: 1.2 meg diskette
+     *  2: 1.44 meg diskette
+     *  3: 2.88 meg diskette
+     *  4: Hard Disk(drive 80)
+     *  5-F:保留
+     * Bit4 - Bit7:保留
+     */
+    UCHAR ucBootMedia;
+
+    /* 启动段,只对x86构架有效,为0默认使用7C0, 注意[1] */
+    UINT16 usLoadSegment;
+
+    /* 是Boot Image里面Partition Table的第5个字节(System Type) */
+    UCHAR ucSystemType;
+
+    UCHAR ucResv;
+
+    /* 启动时每次往内存读多长, 注意[1] */
+    UINT16 usSectorCount;
+
+    /* 启动文件所在的起始逻辑块编号,例如isolinux.bin文件的位置, 注意[1] */
+    UINT32 uiLoadRBA;
+
+    UCHAR aucResv[20];
+}BISO_TORITO_INITIAL_ENTRY_S;
+
+/*
+ * Section Header, 补充一系列可启动的Entry(比如UEFI启动)
+ * 如果默认的Initial/Default Entry不满足BIOS需求
+ * BIOS可以继续往下找,根据Section Header里面的ID
+ * 以及Section Entry里面的Criteria信息决定是否从
+ * 该条Entry启动
+ *
+ */
+typedef struct tagBISO_TORITO_SECHDR_ENTRY
+{
+    /* ucFlag: 0x90:表示后面还有Header, 0x91:表示最后一个Header */
+    UCHAR ucFlag;
+
+    /* 
+     * PlatID: CPU平台架构
+     * 0x00: x86
+     * 0x01: PowerPC
+     * 0x02: Mac
+     * 0xEF: EFI System Partition
+     */
+    UCHAR ucPlatID;
+
+    /* 跟在这个头的后面有多少个Section Entry */
+    UINT16 usSecEntryNum;
+
+    /* ID信息 */
+    CHAR szId[28];
+}BISO_TORITO_SECHDR_ENTRY_S;
+
+/* Section Entry */
+typedef struct tagBISO_TORITO_SECTION_ENTRY
+{
+    /* BOOTID: 88:Bootable, 00:Not Bootable */
+    UCHAR ucBootId;
+    
+    /* 
+     * ucBootMedia:
+     * Bit0 - Bit3的值:
+     *  0: No Emulation
+     *  1: 1.2 meg diskette
+     *  2: 1.44 meg diskette
+     *  3: 2.88 meg diskette
+     *  4: Hard Disk(drive 80)
+     *  5-F:保留
+     * Bit4:保留
+     * Bit5:Continuation Entry Follows
+     * Bit6:Image contains an ATAPI driver
+     * Bit7:Image contains SCSI driver
+     */
+    UCHAR ucFlag;
+
+    /* 启动段,只对x86构架有效,为0默认使用7C0, 注意[1] */
+    UINT16 usLoadSegment;
+
+    /* 是Boot Image里面Partition Table的第5个字节(System Type) */
+    UCHAR ucSystemType;
+
+    UCHAR ucResv;
+
+    /* 启动时每次往内存读多长, 注意[1] */
+    UINT16 usSectorCount;
+
+    /* 启动文件所在的起始逻辑块编号,例如isolinux.bin文件的位置, 注意[1] */
+    UINT32 uiLoadRBA;
+
+    /* 
+     * 它的值描述了后面的aucCriteria的格式
+     * 0: 没有
+     * 1: Language and Version Information (IBM)
+     * 2-FF - Reserved
+     */
+    UCHAR ucCriteriaType;
+
+    /* Criteria信息,如果这19个字节不够用,可以使用Section Extension Entry里的 */
+    UCHAR aucCriteria[19];
+}BISO_TORITO_SECTION_ENTRY_S;
+
+/* 扩展Section Entry */
+typedef struct tagBISO_TORITO_SECEXT_ENTRY
+{
+    /* ucExtId: 必须为44 */
+    UCHAR ucExtId;
+
+    /* 
+     * ucFlag: 只有Bit5有用
+     * Bit5: 1表示后面还有Extension Record  0表示最后一个
+     */
+    UCHAR ucFlag;
+
+    /* Criteria信息 */
+    UCHAR aucCriteria[39];
+}BISO_TORITO_SECEXT_ENTRY_S;
+
+#pragma pack()
+
+/*
+ * 当前缓冲区指针位置向前1条Entry 
+ * 如果已经超过了2048字节,则继续从文件中读
+ */
+#define BISO_ELTORITO_ENTRY_STEP(pstSection, pstFile, aucBuf, stMBuf) \
+{\
+    UINT _uiReadLen;\
+    pstSection++;\
+    if ((UCHAR *)pstSection >= aucBuf + BISO_SECTOR_SIZE)\
+    {\
+        (VOID)BISO_MBUF_Append(&stMBuf, BISO_SECTOR_SIZE, aucBuf);\
+        _uiReadLen = (UINT)BISO_PLAT_ReadFile(pstFile, 1, BISO_SECTOR_SIZE, aucBuf);\
+        if (_uiReadLen != BISO_SECTOR_SIZE)\
+        {\
+            BISO_DIAG("Read Len %u, sector len %u.", _uiReadLen, BISO_SECTOR_SIZE);\
+            BISO_MBUF_Free(&stMBuf);\
+            return BISO_ERR_READ_FILE;\
+        }\
+        pstSection = (BISO_TORITO_SECTION_ENTRY_S *)aucBuf;\
+    }\
+}
+
+ULONG BISO_ELTORITO_ReadBootInfo(IN BISO_FILE_S *pstFile, OUT BISO_PARSER_S *pstParser);
+VOID BISO_ELTORITO_Dump(IN CONST BISO_PARSER_S *pstParser);
+UINT BISO_ELTORITO_GetBootEntryNum(IN CONST BISO_PARSER_S *pstParser);
+
+#endif /* __BISO_ELTORITO_H__ */
+
diff --git a/VtoyTool/BabyISO/biso_joliet.c b/VtoyTool/BabyISO/biso_joliet.c
new file mode 100644 (file)
index 0000000..0235f65
--- /dev/null
@@ -0,0 +1,53 @@
+/******************************************************************************
+ * biso_joliet.c
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "biso.h"
+#include "biso_joliet.h"
+
+UCHAR BISO_JOLIET_GetLevel(IN CONST UCHAR *pucEscape)
+{
+    UCHAR ucLevel = 0;
+    
+    /*
+     * Standard  Level           Decimal        Hex Bytes           ASCII
+     * UCS-2     Level 1     2/5, 2/15, 4/0   (25)(2F)(40)         '%\@'         
+     * UCS-2     Level 2     2/5, 2/15, 4/3   (25)(2F)(43)         '%\C'         
+     * UCS-2     Level 3     2/5, 2/15, 4/5   (25)(2F)(45)         '%\E
+     */
+     
+    if ((NULL != pucEscape) && (0x25 == pucEscape[0]) && (0x2F == pucEscape[1]))
+    {
+        if (0x40 == pucEscape[2])
+        {
+            ucLevel = 1;
+        }
+        else if (0x43 == pucEscape[2])
+        {
+            ucLevel = 2;
+        }
+        else if (0x45 == pucEscape[2])
+        {
+            ucLevel = 3;
+        }
+    }
+
+    return ucLevel;
+}
+
diff --git a/VtoyTool/BabyISO/biso_joliet.h b/VtoyTool/BabyISO/biso_joliet.h
new file mode 100644 (file)
index 0000000..eac1b91
--- /dev/null
@@ -0,0 +1,28 @@
+/******************************************************************************
+ * biso_joliet.h
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __BISO_JOLIET_H__
+#define __BISO_JOLIET_H__
+
+UCHAR BISO_JOLIET_GetLevel(IN CONST UCHAR *pucEscape);
+
+#endif /* __BISO_JOLIET_H__ */
+
diff --git a/VtoyTool/BabyISO/biso_list.c b/VtoyTool/BabyISO/biso_list.c
new file mode 100644 (file)
index 0000000..fb3c807
--- /dev/null
@@ -0,0 +1,98 @@
+/******************************************************************************
+ * biso_list.c
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#include "biso.h"
+#include "biso_list.h"
+#include "biso_util.h"
+
+VOID BISO_DLL_Init(OUT BISO_DLL_S *pstList)
+{
+    pstList->stHead.pstNext = &(pstList->stHead);
+    pstList->stHead.pstPre  = &(pstList->stHead);
+    pstList->pstTail        = &(pstList->stHead);
+    pstList->uiCount        = 0;
+}
+
+VOID BISO_DLL_AddTail
+(
+    IN BISO_DLL_S *pstList, 
+    IN BISO_DLL_NODE_S *pstNode
+)
+{
+    pstList->pstTail->pstNext = pstNode;
+    pstNode->pstNext = NULL;
+    pstNode->pstPre  = pstList->pstTail;
+    pstList->pstTail = pstNode;
+    pstList->uiCount++;
+}
+
+VOID BISO_DLL_DelHead(IN BISO_DLL_S *pstList)
+{
+    BISO_DLL_NODE_S *pstFirst = BISO_DLL_First(pstList);
+    
+    if (NULL != pstFirst)
+    {
+        if (1 == BISO_DLL_Count(pstList))  /* 唯一节点 */
+        {
+            BISO_DLL_Init(pstList);
+        }
+        else
+        {
+            pstFirst->pstNext->pstPre = &(pstList->stHead);
+            pstList->stHead.pstNext = pstFirst->pstNext;
+            pstList->uiCount--;
+        }
+    }
+}
+VOID BISO_DLL_DelTail(IN BISO_DLL_S *pstList)
+{
+    BISO_DLL_NODE_S *pstLast = BISO_DLL_Last(pstList);
+
+    if (NULL != pstLast)
+    {
+        if (1 == BISO_DLL_Count(pstList))  /* 唯一节点 */
+        {
+            BISO_DLL_Init(pstList);
+        }
+        else
+        {
+            pstLast->pstPre->pstNext = NULL;
+            pstList->pstTail = pstLast->pstPre;
+            pstList->uiCount--;
+        }
+    }
+}
+
+VOID BISO_DLL_Free(IN BISO_DLL_S *pstList)
+{
+    BISO_DLL_NODE_S *pstFirst = BISO_DLL_First(pstList);
+
+    while (NULL != pstFirst)
+    {
+        /* 每次都摘掉头节点 */
+        BISO_DLL_DelHead(pstList);
+
+        /* 使用free释放节点 */
+        BISO_FREE(pstFirst);
+        pstFirst = BISO_DLL_First(pstList);
+    }
+}
+
+
diff --git a/VtoyTool/BabyISO/biso_list.h b/VtoyTool/BabyISO/biso_list.h
new file mode 100644 (file)
index 0000000..675fe76
--- /dev/null
@@ -0,0 +1,55 @@
+/******************************************************************************
+ * biso_list.h
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef __BISO_LIST_H__
+#define __BISO_LIST_H__
+
+/* 简单链表的实现 */
+typedef struct tagBISO_DLL_NODE{
+   struct tagBISO_DLL_NODE *pstPre;  /* Points to The Previous Node In The List */
+   struct tagBISO_DLL_NODE *pstNext; /* Points to The Next Node In The List */
+}BISO_DLL_NODE_S;
+
+typedef struct tagBISO_DLL{
+   BISO_DLL_NODE_S  stHead;      /* 链表头 */
+   BISO_DLL_NODE_S *pstTail;     /* 链表尾 */
+   UINT             uiCount;     /* 链表节点个数 */
+}BISO_DLL_S;
+
+#define BISO_DLL_Count(pList)              ((pList)->uiCount)
+#define BISO_DLL_First(pList)              ((BISO_DLL_Count((pList)) == 0) ? NULL: (pList)->stHead.pstNext)
+#define BISO_DLL_Last(pList)               ((BISO_DLL_Count((pList)) == 0) ? NULL : (pList)->pstTail)
+
+#define BISO_DLL_Next(pList, pNode)  \
+            (((pNode) == NULL) ? BISO_DLL_First(pList) : \
+            (((pNode)->pstNext == &(pList)->stHead) ? NULL : (pNode)->pstNext)) 
+
+VOID BISO_DLL_Init(OUT BISO_DLL_S *pstList);
+VOID BISO_DLL_AddTail
+(
+    IN BISO_DLL_S *pstList, 
+    IN BISO_DLL_NODE_S *pstNode
+);
+VOID BISO_DLL_DelHead(IN BISO_DLL_S *pstList);
+VOID BISO_DLL_DelTail(IN BISO_DLL_S *pstList);
+VOID BISO_DLL_Free(IN BISO_DLL_S *pstList);
+
+#endif /* __BISO_LIST_H__ */
+
diff --git a/VtoyTool/BabyISO/biso_plat.h b/VtoyTool/BabyISO/biso_plat.h
new file mode 100644 (file)
index 0000000..ff24898
--- /dev/null
@@ -0,0 +1,29 @@
+/******************************************************************************
+ * biso_plat.h
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifndef __BISO_PLAT_H__
+#define __BISO_PLAT_H__
+
+ULONG BISO_PLAT_Symlink(IN CONST CHAR *pcSrcPath, IN CONST CHAR *pcDstPath);
+
+
+#endif /* __BISO_PLAT_H__ */
+
diff --git a/VtoyTool/BabyISO/biso_plat_linux.c b/VtoyTool/BabyISO/biso_plat_linux.c
new file mode 100644 (file)
index 0000000..5ba42c7
--- /dev/null
@@ -0,0 +1,84 @@
+/******************************************************************************
+ * biso_plat_linux.c
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#ifdef __cplusplus
+extern "C"{
+#endif /* __cplusplus */
+
+#include <limits.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <utime.h>
+#include <unistd.h>
+
+#include "biso.h"
+#include "biso_list.h"
+#include "biso_util.h"
+#include "biso_plat.h"
+
+UINT64 vtoydm_get_file_size(const char *pcFileName);
+BISO_FILE_S * vtoydm_open_file(const char *pcFileName);
+void vtoydm_close_file(BISO_FILE_S *pstFile);
+INT64 vtoydm_seek_file(BISO_FILE_S *pstFile, INT64 i64Offset, INT iFromWhere);
+UINT64 vtoydm_read_file
+(
+    BISO_FILE_S *pstFile, 
+    UINT         uiBlkSize, 
+    UINT         uiBlkNum, 
+    VOID        *pBuf
+);
+
+
+UINT64 BISO_PLAT_GetFileSize(IN CONST CHAR *pcFileName)
+{
+    return vtoydm_get_file_size(pcFileName);  
+}
+
+BISO_FILE_S * BISO_PLAT_OpenExistFile(IN CONST CHAR *pcFileName)
+{
+    return vtoydm_open_file(pcFileName);  
+}
+
+VOID BISO_PLAT_CloseFile(IN BISO_FILE_S *pstFile)
+{
+    return vtoydm_close_file(pstFile);
+}
+
+INT64 BISO_PLAT_SeekFile(BISO_FILE_S *pstFile, INT64 i64Offset, INT iFromWhere)
+{
+    return vtoydm_seek_file(pstFile, i64Offset, iFromWhere);
+}
+
+UINT64 BISO_PLAT_ReadFile
+(
+    IN  BISO_FILE_S *pstFile, 
+    IN  UINT         uiBlkSize, 
+    IN  UINT         uiBlkNum, 
+    OUT VOID        *pBuf
+)
+{
+    return vtoydm_read_file(pstFile, uiBlkSize, uiBlkNum, pBuf);
+}
+
+#ifdef __cplusplus
+}
+#endif /* __cplusplus */
+
diff --git a/VtoyTool/BabyISO/biso_rockridge.c b/VtoyTool/BabyISO/biso_rockridge.c
new file mode 100644 (file)
index 0000000..fd2096c
--- /dev/null
@@ -0,0 +1,567 @@
+/******************************************************************************
+ * biso_rockridge.c
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "biso.h"
+#include "biso_list.h"
+#include "biso_util.h"
+#include "biso_9660.h"
+#include "biso_dump.h"
+#include "biso_rockridge.h"
+
+/* Rock Ridge扩展处理函数数组, NULL表示暂时不处理这类表项 */
+STATIC BISO_RRIP_PARSE_ENTRY_CB_S g_astBISO_RRIP_ParseFunc[] = 
+{
+    { "CE", NULL },
+    { "PD", NULL },
+    { "SP", NULL },
+    { "ST", NULL },
+    { "ER", NULL },
+    { "ES", NULL },
+    { "RR", NULL },
+    { "PX", BISO_RRIP_GetPXInfo },
+    { "PN", BISO_RRIP_GetPNInfo },
+    { "SL", BISO_RRIP_GetSLInfo },
+    { "NM", BISO_RRIP_GetNMInfo },
+    { "CL", NULL },
+    { "PL", NULL },
+    { "RE", NULL },
+    { "TF", BISO_RRIP_GetTFInfo },
+    { "SF", NULL },
+};
+
+STATIC VOID BISO_RRIP_AddLinkBuf
+(
+    IN CHAR *pcBuf,
+    IN UINT  uiBufLen,
+    INOUT BISO_POSIX_INFO_S *pstPosixInfo
+)
+{
+    CHAR *pcNewBuf = NULL;
+    
+    DBGASSERT(NULL != pstPosixInfo);
+    DBGASSERT(NULL != pcBuf);
+
+    /*
+     * 这里采用的是最简单的每次重新申请大内存保存老数据和新数据的方式
+     * 实际上效率是比较低的,但是由于普通ISO文件中link类型本身就不多
+     * 而有连续多条SL表项的就更少了,所以这里只是简单实现功能,没有特别考虑效率
+     */
+
+    /* 申请一个新Buf用于保存原有的数据和这次的新数据 */
+    pcNewBuf = (CHAR *)BISO_ZALLOC(uiBufLen + pstPosixInfo->uiLinkLen);
+    if (NULL == pcNewBuf)
+    {
+        return;
+    }
+
+    if (NULL == pstPosixInfo->pcLinkSrc)
+    {
+        memcpy(pcNewBuf, pcBuf, uiBufLen);
+    }
+    else
+    {
+        /* 分别保存新老数据,同时把老的Buf释放掉 */
+        memcpy(pcNewBuf, pstPosixInfo->pcLinkSrc, pstPosixInfo->uiLinkLen);
+        memcpy(pcNewBuf + pstPosixInfo->uiLinkLen, pcBuf, uiBufLen);
+        BISO_FREE(pstPosixInfo->pcLinkSrc);
+    }
+
+    /* 更新数据Buf */
+    pstPosixInfo->pcLinkSrc = pcNewBuf;
+    pstPosixInfo->uiLinkLen += uiBufLen;
+}
+
+STATIC UINT BISO_RRIP_CalcLinkLen
+(
+    IN CONST CHAR *pcComponet, 
+    IN UINT uiComponetLen
+)
+{
+    UINT uiBufLen = 0;
+    UINT uiOffset = 0;
+    UINT uiTotLen = 0;
+    BISO_RRIP_SL_COMPONENT_S *pstComp = NULL;
+
+    DBGASSERT(NULL != pcComponet);
+    DBGASSERT(uiComponetLen > 0);
+    
+    /* 拼接出链接的源路径 */
+    while (uiOffset < uiComponetLen)
+    {
+        uiBufLen = 0;
+        pstComp = (BISO_RRIP_SL_COMPONENT_S *)(pcComponet + uiOffset);
+
+        if (BOOL_TRUE == BISO_SLCOMP_IS_ROOT(pstComp->ucFlags))
+        {
+            /* ROOT不需处理,后面会添加/ */
+        }
+        else if (BOOL_TRUE == BISO_SLCOMP_IS_CURRENT(pstComp->ucFlags))
+        {
+            uiBufLen = 1; /* . */
+        }
+        else if (BOOL_TRUE == BISO_SLCOMP_IS_PARENT(pstComp->ucFlags))
+        {
+            uiBufLen = 2; /* .. */
+        }
+        else
+        {
+            uiBufLen = pstComp->ucLength;
+        }
+
+        /* ucLength不包括头两个字节 */
+        uiOffset += pstComp->ucLength + 2;
+
+        /*
+         * 如果是link路径的一部分完结,则需要在后面加上'/',否则不加.
+         * 不加的情况有两种: 1是整体已经完了  2是路径的一部分还没有完整
+         * 比如说链接的位置是 /root/xxxx/a.txt 而xxxx可能非常长(200+个字符)
+         * 那么此时光xxxx的部分可能就需要两个Componet部分才能表达完,而这两个
+         * 之间是不能加反斜杠的.
+         */
+        if ((uiOffset < uiComponetLen) && (BOOL_TRUE != BISO_SLCOMP_IS_CONTINUE(pstComp->ucFlags)))
+        {
+            uiBufLen++;
+        }
+
+        uiTotLen += uiBufLen;
+    }
+
+    return uiTotLen;
+}
+
+STATIC UINT BISO_RRIP_GetPartLink
+(
+    IN  CONST BISO_RRIP_SL_COMPONENT_S *pstComponent, 
+    IN  UINT  uiBufSize,
+    OUT CHAR *pcBuf
+)
+{
+    UINT uiBufLen = 0;
+    
+    DBGASSERT(NULL != pstComponent);
+    DBGASSERT(NULL != pcBuf);
+
+    if (BOOL_TRUE == BISO_SLCOMP_IS_ROOT(pstComponent->ucFlags))
+    {
+        /* ROOT不需处理,后面会添加/ */
+    }
+    else if (BOOL_TRUE == BISO_SLCOMP_IS_CURRENT(pstComponent->ucFlags))
+    {
+        scnprintf(pcBuf, uiBufSize, ".");
+        uiBufLen = 1;
+    }
+    else if (BOOL_TRUE == BISO_SLCOMP_IS_PARENT(pstComponent->ucFlags))
+    {
+        scnprintf(pcBuf, uiBufSize, "..");
+        uiBufLen = 2;
+    }
+    else
+    {
+        memcpy(pcBuf, pstComponent->aucData, pstComponent->ucLength);
+        uiBufLen = pstComponent->ucLength;
+    }
+
+    return uiBufLen;
+}
+
+STATIC BOOL_T BISO_RRIP_IsThisType(IN BISO_SUSP_ENTRY_S *pstEntry, IN CONST CHAR *pcType)
+{
+    if (NULL == pstEntry || NULL == pcType)
+    {
+        return BOOL_FALSE;
+    }
+
+    if ((pstEntry->cSignature1 == pcType[0]) && (pstEntry->cSignature2 == pcType[1]))
+    {
+        return BOOL_TRUE;
+    }
+
+    return BOOL_FALSE;
+}
+
+STATIC UCHAR * BISO_RRIP_GetSysUseArea
+(
+    IN  BISO_FILE_S *pstFile, 
+    IN  UCHAR       *pucSysUseField,
+    IN  UINT         uiSysUseFieldLen,
+    OUT UINT        *puiAreaSize
+)
+{
+    UINT uiCELen = 0;
+    UINT uiCurPos = 0;
+    UINT uiReadLen = 0;
+    UINT64 ui64Seek = 0;
+    UCHAR *pucSysUseArea = NULL;
+    BISO_SUSP_ENTRY_S *pstEntry = NULL;
+    BISO_SUSP_ENTRY_CE_S *pstCEEntry = NULL;
+
+    DBGASSERT(NULL != pstFile);
+    DBGASSERT(NULL != pucSysUseField);
+    DBGASSERT(NULL != puiAreaSize);
+
+    /* 
+     * 虽然Rock Ridge扩展标准中允许整个System Use Area中有多个CE表项来扩展,
+     * 但是由于一条CE表项可以扩展的长度就足够了(32bit) 所以这里我感觉正常情况下
+     * 没有必要使用多个CE表项扩展空间。因此这里只支持1条CE表项的情况。
+     */
+
+    /* 遍历当前System Use Field, 标准规定剩余空间小于4字节,则后面的忽略 */
+    for (uiCurPos = 0; uiCurPos + 4 < uiSysUseFieldLen; uiCurPos += pstEntry->ucEntryLen)
+    {
+        pstEntry = (BISO_SUSP_ENTRY_S *)(pucSysUseField + uiCurPos);
+
+        /* 找到1个CE表项就停止,这里默认CE表项是最后一条表项 */
+        if (BOOL_TRUE == BISO_RRIP_IsThisType(pstEntry, "CE"))
+        {
+            pstCEEntry = (BISO_SUSP_ENTRY_CE_S *)pstEntry;
+            uiCELen = pstCEEntry->uiContinuationLen;
+            /* BISO_DUMP_ShowSUSPEntry(pstCEEntry); */
+            break;
+        }
+    }
+
+    /* 申请一块内存把这两部分合并起来 */
+    pucSysUseArea = (UCHAR *)BISO_MALLOC(uiCurPos + uiCELen);
+    if (NULL == pucSysUseArea)
+    {
+        return NULL;
+    }
+
+    /* 先拷贝System Use Field字段 */
+    memcpy(pucSysUseArea, pucSysUseField, uiCurPos);
+
+    /* 如果有CE表项则再同文件中读出CE部分的数据 */
+    if (NULL != pstCEEntry)
+    {
+        ui64Seek = (UINT64)pstCEEntry->uiBlockLoc * BISO_BLOCK_SIZE + pstCEEntry->uiByteOffset;
+        BISO_PLAT_SeekFile(pstFile, ui64Seek, SEEK_SET);
+        uiReadLen = (UINT)BISO_PLAT_ReadFile(pstFile, 1, uiCELen, pucSysUseArea + uiCurPos);
+        if (uiReadLen != uiCELen)
+        {
+            BISO_DIAG("Read len %u buf len %u.", uiReadLen, uiCELen);
+            BISO_FREE(pucSysUseArea);
+            return NULL;
+        }
+    }
+
+    *puiAreaSize = uiCurPos + uiCELen;
+    return pucSysUseArea;
+}
+
+VOID BISO_RRIP_GetPXInfo(IN VOID *pEntry, OUT BISO_DIR_TREE_S *pstDirTree)
+{
+    BISO_POSIX_INFO_S *pstPosixInfo = NULL;
+    BISO_ROCK_RIDGE_ENTRY_PX_S *pstPXEntry = NULL;
+
+    DBGASSERT(NULL != pEntry);
+    DBGASSERT(NULL != pstDirTree);
+    DBGASSERT(NULL != pstDirTree->pstPosixInfo);
+
+    pstPXEntry = (BISO_ROCK_RIDGE_ENTRY_PX_S *)pEntry;
+    pstPosixInfo = pstDirTree->pstPosixInfo;
+
+    pstPosixInfo->uiPosixFileMode    = pstPXEntry->uiPosixFileMode;
+    pstPosixInfo->uiPosixFileLink    = pstPXEntry->uiPosixFileLink;
+    pstPosixInfo->uiPosixFileUserId  = pstPXEntry->uiPosixFileUserId;
+    pstPosixInfo->uiPosixFileGroupId = pstPXEntry->uiPosixFileGroupId;
+    pstPosixInfo->uiPosixFileSNO     = pstPXEntry->uiPosixFileSNO;
+}
+
+VOID BISO_RRIP_GetNMInfo(IN VOID *pEntry, OUT BISO_DIR_TREE_S *pstDirTree)
+{
+    BISO_ROCK_RIDGE_ENTRY_NM_S *pstNMEntry = NULL;
+
+    DBGASSERT(NULL != pEntry);
+    DBGASSERT(NULL != pstDirTree);
+    DBGASSERT(NULL != pstDirTree->pstPosixInfo);
+
+    pstNMEntry = (BISO_ROCK_RIDGE_ENTRY_NM_S *)pEntry;
+
+    /* 如有NM表项就替换ISO9660文件名 */
+    if (BOOL_TRUE != pstDirTree->pstPosixInfo->bHasNMEntry)
+    {
+        pstDirTree->pstPosixInfo->bHasNMEntry = BOOL_TRUE;
+        memset(pstDirTree->szName, 0, sizeof(pstDirTree->szName));
+        pstDirTree->usNameLen = 0;
+    }
+    
+    /*
+     * 拼接文件名, 有可能本函数会多次调用,多次拼接(文件名超长的情况) 
+     * TODO: 是否需要关注字符编码???
+     */
+    strncat(pstDirTree->szName, pstNMEntry->szFileName, pstNMEntry->ucEntryLen - 5);
+    pstDirTree->usNameLen += pstNMEntry->ucEntryLen - 5;
+}
+
+VOID BISO_RRIP_GetTFInfo(IN VOID *pEntry, OUT BISO_DIR_TREE_S *pstDirTree)
+{
+    UINT i;
+    UCHAR *pucCur = NULL;
+    BISO_DATE_915_S *pst915Date = NULL;
+    BISO_ROCK_RIDGE_ENTRY_TF_S *pstTFEntry = NULL;
+    BISO_DATE_S *apstDate[] = 
+    {
+        &(pstDirTree->pstPosixInfo->stCreateTime),
+        &(pstDirTree->pstPosixInfo->stModifyTime),
+        &(pstDirTree->pstPosixInfo->stLastAccessTime),
+        &(pstDirTree->pstPosixInfo->stLastAttrChangeTime),
+        &(pstDirTree->pstPosixInfo->stLastBackupTime),
+        &(pstDirTree->pstPosixInfo->stExpirationTime),
+        &(pstDirTree->pstPosixInfo->stEffectiveTime)
+    };
+
+    DBGASSERT(NULL != pEntry);
+    DBGASSERT(NULL != pstDirTree);
+    DBGASSERT(NULL != pstDirTree->pstPosixInfo);
+
+    pstTFEntry = (BISO_ROCK_RIDGE_ENTRY_TF_S *)pEntry;
+    pucCur = pstTFEntry->aucTimeStamp;
+
+    for (i = 0; i < ARRAY_SIZE(apstDate); i++)
+    {
+        /* 比特位0说明该时间戳没有记录 */
+        if (0 == ((pstTFEntry->ucFlags >> i) & 0x1))
+        {
+            continue;
+        }
+
+        /* Bit7决定是按照哪种格式记录的 */
+        if ((pstTFEntry->ucFlags >> 7) & 0x1)
+        {
+            (VOID)BISO_9660_ParseDate84261((CHAR *)pucCur, apstDate[i]);
+            pucCur += 17;
+        }
+        else
+        {
+            pst915Date = (BISO_DATE_915_S *)pucCur;
+            pucCur += 7;
+
+            apstDate[i]->usYear    = pst915Date->ucYear + 1900;
+            apstDate[i]->ucMonth   = pst915Date->ucMonth;
+            apstDate[i]->ucDay     = pst915Date->ucDay;
+            apstDate[i]->ucHour    = pst915Date->ucHour;
+            apstDate[i]->ucMin     = pst915Date->ucMin;
+            apstDate[i]->ucSecond  = pst915Date->ucSec;
+            apstDate[i]->usMillSec = 0;
+            apstDate[i]->cZone     = pst915Date->cTimeZone / 4;
+        }
+    }
+}
+
+VOID BISO_RRIP_GetPNInfo(IN VOID *pEntry, OUT BISO_DIR_TREE_S *pstDirTree)
+{
+    BISO_ROCK_RIDGE_ENTRY_PN_S *pstPNEntry = NULL;
+
+    DBGASSERT(NULL != pEntry);
+    DBGASSERT(NULL != pstDirTree);
+    DBGASSERT(NULL != pstDirTree->pstPosixInfo);
+
+    pstPNEntry = (BISO_ROCK_RIDGE_ENTRY_PN_S *)pEntry;
+    
+    pstDirTree->pstPosixInfo->ui64DevNum = ((UINT64)(pstPNEntry->uiDevNumHigh) << 32) | pstPNEntry->uiDevNumLow;
+}
+
+VOID BISO_RRIP_GetSLInfo(IN VOID *pEntry, OUT BISO_DIR_TREE_S *pstDirTree)
+{
+    UINT  uiBufLen = 0;
+    UINT  uiOffset = 0;
+    UINT  uiCurPos = 0;
+    UCHAR ucCompentLen = 0;
+    CHAR *pcFullLinkPath = NULL;
+    BISO_POSIX_INFO_S *pstPosixInfo = NULL;
+    BISO_ROCK_RIDGE_ENTRY_SL_S *pstSLEntry = NULL;
+    BISO_RRIP_SL_COMPONENT_S *pstComp = NULL;
+    CHAR szBuf[300]; /* 当前Length是用UCHAR存储的,一定不会超过300 */
+
+    DBGASSERT(NULL != pEntry);
+    DBGASSERT(NULL != pstDirTree);
+    DBGASSERT(NULL != pstDirTree->pstPosixInfo);
+
+    pstSLEntry = (BISO_ROCK_RIDGE_ENTRY_SL_S *)pEntry;
+    pstPosixInfo = pstDirTree->pstPosixInfo;
+    ucCompentLen = pstSLEntry->ucEntryLen - 5;
+
+    /*
+     * 把所有SL表项的Componet部分拼接起来,如果有连续几条SL表项
+     * 那么当前函数会依次被调用,每次都拼接一部分,直到整个Componet整合完成.
+     */
+    BISO_RRIP_AddLinkBuf((CHAR *)(pstSLEntry->aucComponet), ucCompentLen, pstPosixInfo);
+
+    /* FLAG的Bit0为0表示是最后1个SL表项,此时Componet已经整合在一起了,这里直接处理 */
+    if (0 == (pstSLEntry->ucFlags & 0x1))
+    {
+        /* 申请一段内存用来保存符号链接的源路径 */
+        uiBufLen = BISO_RRIP_CalcLinkLen(pstPosixInfo->pcLinkSrc, pstPosixInfo->uiLinkLen);
+        pcFullLinkPath = (CHAR *)BISO_MALLOC(uiBufLen + 10);
+        if (NULL == pcFullLinkPath)
+        {
+            BISO_FREE(pstPosixInfo->pcLinkSrc);
+            pstPosixInfo->uiLinkLen = 0;
+            return;
+        }
+
+        /* 拼接出链接的源路径 */
+        while (uiOffset < pstPosixInfo->uiLinkLen)
+        {
+            pstComp = (BISO_RRIP_SL_COMPONENT_S *)(pstPosixInfo->pcLinkSrc + uiOffset);
+            uiBufLen = BISO_RRIP_GetPartLink(pstComp, sizeof(szBuf), szBuf);
+
+            /* ucLength不包括头两个字节 */
+            uiOffset += pstComp->ucLength + 2;
+
+            /*
+             * 如果是link路径的一部分完结,则需要在后面加上'/',否则不加.
+             * 不加的情况有两种: 1是整体已经完了  2是路径的一部分还没有完整
+             * 比如说链接的位置是 /root/xxxx/a.txt 而xxxx可能非常长(200+个字符)
+             * 那么此时光xxxx的部分可能就需要两个Componet部分才能表达完,而这两个
+             * 之间是不能加反斜杠的.
+             */
+            if ((uiOffset < pstPosixInfo->uiLinkLen) && (BOOL_TRUE != BISO_SLCOMP_IS_CONTINUE(pstComp->ucFlags)))
+            {
+                szBuf[uiBufLen++] = '/';
+            }
+
+            memcpy(pcFullLinkPath + uiCurPos, szBuf, uiBufLen);
+            uiCurPos += uiBufLen;
+        }
+
+        pcFullLinkPath[uiCurPos++] = 0;
+
+        /* 原来的内存释放掉 */
+        BISO_FREE(pstPosixInfo->pcLinkSrc);
+        pstPosixInfo->pcLinkSrc = pcFullLinkPath;
+        pstPosixInfo->uiLinkLen = uiCurPos;
+    }
+}
+
+ULONG BISO_RRIP_ReadExtInfo
+(
+    IN  BISO_FILE_S       *pstFile,
+    IN  BISO_PARSER_S     *pstParser,
+    IN  BISO_DIR_RECORD_S *pstRecord, 
+    OUT BISO_DIR_TREE_S   *pstDirTree 
+)
+{
+    UINT   i = 0;
+    UINT   uiOffset = 0;
+    UINT   uiAreaSize = 0;
+    UCHAR *pucSysUseArea = NULL;
+    BISO_SUSP_ENTRY_S *pstEntry = NULL;
+
+    DBGASSERT(NULL != pstFile);
+    DBGASSERT(NULL != pstParser);
+    DBGASSERT(NULL != pstRecord);
+    DBGASSERT(NULL != pstDirTree);
+
+    /* 没有使用Rock Ridge扩展则直接返回 */
+    if (0 == pstParser->ucRRIPVersion)
+    {
+        return BISO_SUCCESS;
+    }
+
+    /* 先申请POSIX INFO结构体内存 */
+    if (NULL == pstDirTree->pstPosixInfo)
+    {
+        pstDirTree->pstPosixInfo = (BISO_POSIX_INFO_S *)BISO_ZALLOC(sizeof(BISO_POSIX_INFO_S));
+        if (NULL == pstDirTree->pstPosixInfo)
+        {
+            return BISO_ERR_ALLOC_MEM;
+        }
+    }
+
+    /* 偏移到System Use字段所在的位置,注意Padding字段, 保证偏移为偶数 */
+    uiOffset = 33 + pstRecord->ucNameLen;
+    uiOffset += uiOffset & 0x1;
+
+    /* 再加上SP Entry中定义的SkipLen长度 */
+    uiOffset += pstParser->ucRRIPSkipLen;
+
+    /* 获取整个Syetem Use区域的数据(包括CE扩展区) */
+    pucSysUseArea = BISO_RRIP_GetSysUseArea(pstFile, (UCHAR *)pstRecord + uiOffset, 
+                                            pstRecord->ucLength - (UCHAR)uiOffset, &uiAreaSize);
+    if (NULL == pucSysUseArea)
+    {
+        return BISO_ERR_ALLOC_MEM;
+    }
+
+    /* 遍历所有的RRIP表项 */
+    for(uiOffset = 0; uiOffset + 4 < uiAreaSize; uiOffset += pstEntry->ucEntryLen)
+    {
+        pstEntry = (BISO_SUSP_ENTRY_S *)(pucSysUseArea + uiOffset);
+        /* BISO_DUMP_ShowSUSPEntry(pstEntry); */
+
+        /* 找到对应的处理函数处理 */
+        for (i = 0; i < ARRAY_SIZE(g_astBISO_RRIP_ParseFunc); i++)
+        {
+            if (BOOL_TRUE == BISO_RRIP_IsThisType(pstEntry, g_astBISO_RRIP_ParseFunc[i].szSignature))
+            {
+                if (NULL != g_astBISO_RRIP_ParseFunc[i].pfFunc)
+                {
+                    g_astBISO_RRIP_ParseFunc[i].pfFunc(pstEntry, pstDirTree);
+                }
+                break;
+            }
+        }
+    }
+
+    BISO_FREE(pucSysUseArea);
+    return BISO_SUCCESS;
+}
+
+ULONG BISO_RRIP_ReadIndicator(INOUT BISO_PARSER_S *pstParser)
+{
+    ULONG ulRet;
+    UINT64 ui64Seek = 0;
+    BISO_DIR_RECORD_S *pstRootDir = NULL;
+    BISO_SUSP_ENTRY_SP_S *pstSPEntry = NULL;
+    UCHAR aucBuf[sizeof(BISO_DIR_RECORD_S) + sizeof(BISO_SUSP_ENTRY_SP_S)];
+    
+    DBGASSERT(NULL != pstParser);
+
+    /* 读出Root Directory Record */
+    pstRootDir = &(pstParser->pstPVD->stRootDirRecord);
+    ui64Seek = BISO_BLOCK_SIZE * (UINT64)pstRootDir->uiExtent;
+    ulRet = BISO_UTIL_ReadFile(pstParser->szFileName, ui64Seek, sizeof(aucBuf), aucBuf);
+    if (BISO_SUCCESS != ulRet)
+    {
+        return ulRet;
+    }
+
+    /* 看看Root Directory Record的System Use字段里有没有SP Entry */
+    pstRootDir = (BISO_DIR_RECORD_S *)aucBuf;
+    pstSPEntry = (BISO_SUSP_ENTRY_SP_S *)(pstRootDir + 1);
+    if (('S' != pstSPEntry->cSignature1) || ('P' != pstSPEntry->cSignature2) ||
+        (0xBE != pstSPEntry->ucChkBE) || (0xEF != pstSPEntry->ucChkEF))
+    {
+        pstParser->ucRRIPVersion = 0;
+        pstParser->ucRRIPSkipLen = 0;
+    }
+    else
+    {
+        pstParser->ucRRIPVersion = pstSPEntry->ucVersion;
+        pstParser->ucRRIPSkipLen = pstSPEntry->ucSkipLen;       
+    }
+    
+    return BISO_SUCCESS;
+}
+
+
+
diff --git a/VtoyTool/BabyISO/biso_rockridge.h b/VtoyTool/BabyISO/biso_rockridge.h
new file mode 100644 (file)
index 0000000..61aa08e
--- /dev/null
@@ -0,0 +1,500 @@
+/******************************************************************************
+ * biso_rockridge.h
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+
+/*
+ * 本文件中定义了SUSP(ystem Use Sharing Protocol)协议(IEEE P1281)
+ * 以及Rock Ridge 扩展中定义的各种表项结构(IEEE P1282)
+ * 其中SUSP是规定了ISO-9660中Directory Record中System Use字段的使用方法
+ * Rock Ridge是基于SUSP,在System Use字段中扩展记录了文件的属性、名称等POSIX文件信息
+ * 详细的说明请参考标准文档(IEEE P1281、IEEE P1282)
+ * SUSP定义的每一条Entry都是不定长的,其长度在结构里面描述
+ */
+#ifndef __BISO_ROCKRIDGE_H__
+#define __BISO_ROCKRIDGE_H__
+
+#ifndef S_IFLNK
+#define  S_IFLNK 0120000 
+#define  S_IFREG 0100000
+#define  S_IFDIR 0040000 
+#endif
+
+typedef VOID (* BISO_DUMP_ENTRY_PF)(IN VOID *pEntry);
+
+typedef struct tagBISO_DUMP_ENTRY_CB
+{
+    CHAR szSignature[3];
+    BISO_DUMP_ENTRY_PF pfDump;
+}BISO_DUMP_ENTRY_CB_S;
+
+typedef VOID (* BISO_RRIP_PARSE_ENTRY_PF)
+(
+    IN VOID *pEntry,
+    OUT BISO_DIR_TREE_S *pstDirTree
+);
+
+typedef struct tagBISO_RRIP_PARSE_ENTRY_CB
+{
+    CHAR szSignature[3];
+    BISO_RRIP_PARSE_ENTRY_PF pfFunc;
+}BISO_RRIP_PARSE_ENTRY_CB_S;
+
+
+#pragma pack(1)
+
+/* SUSP: System Use Sharing Protocol */
+
+/* SUSP中定义的标准Entry结构 */
+typedef struct tagBISO_SUSP_ENTRY
+{
+    /* 两个控制字 */
+    CHAR cSignature1;
+    CHAR cSignature2;
+
+    /* 长度,包括控制字 */
+    UCHAR ucEntryLen;
+
+    /* 版本号 */
+    UCHAR ucVersion;
+
+    /* 数据,具体长度有ucEntryLen确定 */
+    UCHAR aucData[1];        
+}BISO_SUSP_ENTRY_S;
+
+/* 
+ * Continuation Area(可选)
+ * !!!!!!!!!!!!!!!!!
+ * !!!!!!!!!!!!!!!!!
+ * !!!!!!!!!!!!!!!!!
+ * 之所以有CE这个控制字是因为ISO-9660标准定义的Directory Record
+ * 的长度是用1个字节表示的.最大就255 Byte,所以System Use字段的长度也就
+ * 不可能大于255,而有些扩展信息可能需要很多个Entry才能描述,255的长度不够
+ * 就需要扩展,所以才有了这个用于扩展的Entry格式
+ * 每个Directory Record的System Use字段或者扩展区域中只能最多有1个CE表项,
+ * 而且一般也应该是最后1条表项,不过整个System Use区域的CE表项个数的没有限制的。
+ */
+typedef struct tagBISO_SUSP_ENTRY_CE
+{
+    /* 两个控制字 */
+    CHAR  cSignature1;  /* 必须为 'C' */
+    CHAR  cSignature2;  /* 必须为 'E' */
+
+    /* 长度,包括控制字, 必须为28 */
+    UCHAR ucEntryLen;
+
+    /* 版本号, 必须为1 */
+    UCHAR ucVersion;
+
+    BISO_DEF_733(uiBlockLoc)
+    BISO_DEF_733(uiByteOffset)
+    BISO_DEF_733(uiContinuationLen)
+}BISO_SUSP_ENTRY_CE_S;
+
+/* Padding Filed(可选) */
+typedef struct tagBISO_SUSP_ENTRY_PD
+{
+    /* 两个控制字 */
+    CHAR  cSignature1;  /* 必须为 'P' */
+    CHAR  cSignature2;  /* 必须为 'D' */
+
+    /* 长度,包括控制字 */
+    UCHAR ucEntryLen;
+
+    /* 版本号, 必须为1 */
+    UCHAR ucVersion;
+
+    /* 数据,具体长度有ucEntryLen确定 */
+    UCHAR aucData[1];     
+}BISO_SUSP_ENTRY_PD_S;
+
+/*
+ * System Use Sharing Protocol Indicator(必须存在)
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
+ * SP必须是在Root Directory Record的System Use里面从1个字节开始的第1条表项
+ * 另外,SP表项还有一个作用就是它里面的Skip Len字段,它的意思是
+ * 在除了ROOT之外的所有Directory Record中,System Use字段不一定是从第1个字节开始就是
+ * SUSP的各种表项,可以统一跳过一定的偏移(为了兼容性考虑),就是这里的Skip Len
+ * 注意这个Skip Len对ROOT根目录无效,对于ROOT永远是从SYSTEM Use的第1个字节开始是SP Entry
+ */
+typedef struct tagBISO_SUSP_ENTRY_SP
+{
+    /* 两个控制字 */
+    CHAR  cSignature1;  /* 必须为 'S' */
+    CHAR  cSignature2;  /* 必须为 'P' */
+
+    /* 长度,包括控制字, 必须为7 */
+    UCHAR ucEntryLen;
+
+    /* 版本号, 必须为1 */
+    UCHAR ucVersion;
+
+    /* 检查字段 */
+    UCHAR ucChkBE;     
+    UCHAR ucChkEF;     
+    UCHAR ucSkipLen;
+}BISO_SUSP_ENTRY_SP_S;
+
+/* System Use Sharing Protocol Termonitor: System Use或者CE区域最后一条表项 */
+typedef struct tagBISO_SUSP_ENTRY_ST
+{
+    /* 两个控制字 */
+    CHAR  cSignature1;  /* 必须为 'S' */
+    CHAR  cSignature2;  /* 必须为 'T' */
+
+    /* 长度,包括控制字, 必须为4 */
+    UCHAR ucEntryLen;
+
+    /* 版本号, 必须为1 */
+    UCHAR ucVersion;
+}BISO_SUSP_ENTRY_ST_S;
+
+/* Extensions Reference */
+typedef struct tagBISO_SUSP_ENTRY_ER
+{
+    /* 两个控制字 */
+    CHAR  cSignature1;  /* 必须为 'E' */
+    CHAR  cSignature2;  /* 必须为 'R' */
+
+    /* 长度,包括控制字 */
+    UCHAR ucEntryLen;
+
+    /* 版本号, 必须为1 */
+    UCHAR ucVersion;
+
+    UCHAR ucIdLen;
+
+    UCHAR ucDescLen;
+
+    UCHAR ucSrcLen;
+
+    UCHAR ucExtVer;
+
+    /* 后面跟着数据 */
+    /* UCHAR aucId[ucIdLen]; */
+    /* UCHAR aucDesc[ucDescLen]; */
+    /* UCHAR aucSrc[ucSrcLen]; */
+}BISO_SUSP_ENTRY_ER_S;
+
+/* Extension Selector */
+typedef struct tagBISO_SUSP_ENTRY_ES
+{
+    /* 两个控制字 */
+    CHAR  cSignature1;  /* 必须为 'E' */
+    CHAR  cSignature2;  /* 必须为 'S' */
+
+    /* 长度,包括控制字, 必须为5 */
+    UCHAR ucEntryLen;
+
+    /* 版本号, 必须为1 */
+    UCHAR ucVersion;
+
+    /* 扩展编号 */
+    UCHAR ucExtSeq;
+}BISO_SUSP_ENTRY_ES_S;
+
+/* Rock Ridge In Use RRIP 1991A版本里定义的结构, 新版本已经废弃 */
+typedef struct tagBISO_ROCK_RIDGE_ENTRY_RR
+{
+    /* 两个控制字 */
+    CHAR  cSignature1;  /* 必须为 'R' */
+    CHAR  cSignature2;  /* 必须为 'R' */
+
+    /* 长度,包括控制字, 必须为5 */
+    UCHAR ucEntryLen;
+
+    /* 版本号, 必须为1 */
+    UCHAR ucVersion;
+
+    /*
+     * ucFlags, 各Bit含义如下:
+     * Bit0: "PX" System Use Field recorded
+     * Bit1: "PN" System Use Field recorded
+     * Bit2: "SL" System Use Field recorded
+     * Bit3: "NM" System Use Field recorded
+     * Bit4: "CL" System Use Field recorded
+     * Bit5: "PL" System Use Field recorded
+     * Bit6: "RE" System Use Field recorded
+     * Bit7: "TF" System Use Field recorded
+     */
+    UCHAR ucFlags;
+}BISO_ROCK_RIDGE_ENTRY_RR_S;
+
+
+/* POSIX file attributes */
+typedef struct tagBISO_ROCK_RIDGE_ENTRY_PX
+{
+    /* 两个控制字 */
+    CHAR  cSignature1;  /* 必须为 'P' */
+    CHAR  cSignature2;  /* 必须为 'X' */
+
+    /* 长度,包括控制字, 必须为44
+     * !!!!!!!!!!!!!!!!!!!!!!!!!! 
+     * !!!!!!!!!!!!!!!!!!!!!!!!!! 
+     * !!!!!!!!!!!!!!!!!!!!!!!!!! 
+     * !!!!!!!!!!!!!!!!!!!!!!!!!! 
+     * IEEE P1282 V1.10之前是36不是44
+     */
+    UCHAR ucEntryLen;
+
+    /* 版本号, 必须为1 */
+    UCHAR ucVersion;
+
+    /* File Mode的比特位图, 就是linux里面stat结构里面的st_mode字段 */
+    BISO_DEF_733(uiPosixFileMode)
+
+    /* st_nlink字段 */
+    BISO_DEF_733(uiPosixFileLink)
+    
+    /* st_uid字段 */
+    BISO_DEF_733(uiPosixFileUserId)
+    
+    /* st_gid字段 */
+    BISO_DEF_733(uiPosixFileGroupId)
+    
+    /* st_ino字段, 注意这个字段在IEEE P1282 V1.10之前是没有的 */
+    BISO_DEF_733(uiPosixFileSNO)
+}BISO_ROCK_RIDGE_ENTRY_PX_S;
+
+/* POSIX device number */
+typedef struct tagBISO_ROCK_RIDGE_ENTRY_PN
+{
+    /* 两个控制字 */
+    CHAR  cSignature1;  /* 必须为 'P' */
+    CHAR  cSignature2;  /* 必须为 'N' */
+
+    /* 长度,包括控制字, 必须为20 */
+    UCHAR ucEntryLen;
+
+    /* 版本号, 必须为1 */
+    UCHAR ucVersion;
+
+    /* Device Number的高低32位 */
+    BISO_DEF_733(uiDevNumHigh)
+    BISO_DEF_733(uiDevNumLow)
+}BISO_ROCK_RIDGE_ENTRY_PN_S;
+
+/* Symbol Link */
+typedef struct tagBISO_ROCK_RIDGE_ENTRY_SL
+{
+    /* 两个控制字 */
+    CHAR  cSignature1;  /* 必须为 'S' */
+    CHAR  cSignature2;  /* 必须为 'L' */
+
+    /* 长度,包括控制字, 必须为5+Componet长度 */
+    UCHAR ucEntryLen;
+
+    /* 版本号, 必须为1 */
+    UCHAR ucVersion;
+
+    /* 
+     * 0:最后一个软链接 
+     * 1:后面还有
+     */
+    UCHAR ucFlags;
+
+    /* Componet内容,有具体的格式定义ISO_RRIP_SL_COMPONENT_S */
+    UCHAR aucComponet[1];    
+}BISO_ROCK_RIDGE_ENTRY_SL_S;
+
+typedef struct tagBISO_RRIP_SL_COMPONENT
+{
+    /* ucFlags 
+     * Bit0: Continue 
+     * Bit1: Current当前目录 '.'
+     * Bit2: Parent父目录 '..'
+     * Bit3: Root 目录 '/'
+     * 其他位保留
+     */
+    UCHAR ucFlags;
+
+    /* 长度,不包括ucFlags和自己,纯粹是后面aucData的长度 */
+    UCHAR ucLength;
+
+    UCHAR aucData[1];
+}BISO_RRIP_SL_COMPONENT_S;
+
+#define BISO_SLCOMP_IS_CONTINUE(ucFlag) (((ucFlag >> 0) & 0x1) > 0 ? BOOL_TRUE : BOOL_FALSE)
+#define BISO_SLCOMP_IS_CURRENT(ucFlag)  (((ucFlag >> 1) & 0x1) > 0 ? BOOL_TRUE : BOOL_FALSE)
+#define BISO_SLCOMP_IS_PARENT(ucFlag)   (((ucFlag >> 2) & 0x1) > 0 ? BOOL_TRUE : BOOL_FALSE)
+#define BISO_SLCOMP_IS_ROOT(ucFlag)     (((ucFlag >> 3) & 0x1) > 0 ? BOOL_TRUE : BOOL_FALSE)
+
+/*
+ * Alternate Name 用来记录POSIX文件名
+ * 注意NM表项可以有很多个,一直到ucFlags的比特0为0
+ * 多个NM表项里面的szFileName部分要拼接在一起表示整个完整的文件名
+ * 这个应该主要是为了解决长文件名的问题
+ */
+typedef struct tagBISO_ROCK_RIDGE_ENTRY_NM
+{
+    /* 两个控制字 */
+    CHAR  cSignature1;  /* 必须为 'N' */
+    CHAR  cSignature2;  /* 必须为 'M' */
+
+    /* 长度,包括控制字, 必须为5+Name长度 */
+    UCHAR ucEntryLen;
+
+    /* 版本号, 必须为1 */
+    UCHAR ucVersion;
+
+    /* 
+     * ucFlags 
+     * Bit0: Continue 
+     * Bit1: Current当前目录 '.'
+     * Bit2: Parent父目录 '..'
+     * 其他位保留
+     */
+    UCHAR ucFlags;
+
+    /* 后面跟着具体的名称,如果Flag的Bit1 2 5置为则后面就没有了 */
+    CHAR szFileName[1];
+}BISO_ROCK_RIDGE_ENTRY_NM_S;
+
+
+/* 后面的CL PL RE用来扩展超过8级目录的情况 */
+
+/* Child Link */
+typedef struct tagBISO_ROCK_RIDGE_ENTRY_CL
+{
+    /* 两个控制字 */
+    CHAR  cSignature1;  /* 必须为 'C' */
+    CHAR  cSignature2;  /* 必须为 'L' */
+
+    /* 长度,包括控制字, 必须为12 */
+    UCHAR ucEntryLen;
+
+    /* 版本号, 必须为1 */
+    UCHAR ucVersion;
+
+    BISO_DEF_733(uiChildDirLoc)
+}BISO_ROCK_RIDGE_ENTRY_CL_S;
+
+/* Parent Link */
+typedef struct tagBISO_ROCK_RIDGE_ENTRY_PL
+{
+    /* 两个控制字 */
+    CHAR  cSignature1;  /* 必须为 'P' */
+    CHAR  cSignature2;  /* 必须为 'L' */
+
+    /* 长度,包括控制字, 必须为12 */
+    UCHAR ucEntryLen;
+
+    /* 版本号, 必须为1 */
+    UCHAR ucVersion;
+
+    BISO_DEF_733(uiParentDirLoc)
+}BISO_ROCK_RIDGE_ENTRY_PL_S;
+
+/* Relocated Directory */
+typedef struct tagBISO_ROCK_RIDGE_ENTRY_RE
+{
+    /* 两个控制字 */
+    CHAR  cSignature1;  /* 必须为 'R' */
+    CHAR  cSignature2;  /* 必须为 'E' */
+
+    /* 长度,包括控制字, 必须为4 */
+    UCHAR ucEntryLen;
+
+    /* 版本号, 必须为1 */
+    UCHAR ucVersion;
+}BISO_ROCK_RIDGE_ENTRY_RE_S;
+
+/* Time Stamps For File */
+typedef struct tagBISO_ROCK_RIDGE_ENTRY_TF
+{
+    /* 两个控制字 */
+    CHAR  cSignature1;  /* 必须为 'T' */
+    CHAR  cSignature2;  /* 必须为 'F' */
+
+    /* 长度,包括控制字  */
+    UCHAR ucEntryLen;
+
+    /* 版本号, 必须为1 */
+    UCHAR ucVersion;
+
+    /*
+     * ucFlags:
+     * Bit0: Create Time  是否记录
+     * Bit1: Modify Time  是否记录
+     * Bit2: Last Access Time  是否记录
+     * Bit3: Last Attribute Change Time  是否记录
+     * Bit4: Last Backup Time  是否记录
+     * Bit5: Expiration Time   是否记录
+     * Bit6: Effective Time    是否记录
+     * Bit7: Long-Form 时间格式 
+     *       0 表示7字节数组格式(ECMA-119 9.1.5)  
+     *       1 表示17字符串格式(ECMA-119 8.4.26.1)
+     */
+    UCHAR ucFlags;
+
+    /* 具体的每一个时间戳,每个时间戳7字节或者17字节,取决于ucFlags的Bit7 */
+    UCHAR aucTimeStamp[1];
+}BISO_ROCK_RIDGE_ENTRY_TF_S;
+
+/* File Data in sparse format 稀疏文件 */
+typedef struct tagBISO_ROCK_RIDGE_ENTRY_SF
+{
+    /* 两个控制字 */
+    CHAR  cSignature1;  /* 必须为 'S' */
+    CHAR  cSignature2;  /* 必须为 'F' */
+
+    /* 长度,包括控制字, 必须为21 */
+    UCHAR ucEntryLen;
+
+    /* 版本号, 必须为1 */
+    UCHAR ucVersion;
+
+    BISO_DEF_733(uiVirFileSizeHigh)
+    BISO_DEF_733(uiVirFileSizeLow)
+
+    /*
+     * ucDepth: 
+     * 1-->64KB
+     * 2-->16MB
+     * 3-->4GB
+     * 4-->1TB
+     * 5-->256TB
+     * 6-->64K TB
+     * 7-->16M TB
+     */
+    UCHAR ucDepth;
+}BISO_ROCK_RIDGE_ENTRY_SF_S;
+
+#pragma pack()
+
+ULONG BISO_RRIP_ReadIndicator(INOUT BISO_PARSER_S *pstParser);
+ULONG BISO_RRIP_ReadExtInfo
+(
+    IN  BISO_FILE_S       *pstFile,
+    IN  BISO_PARSER_S     *pstParser,
+    IN  BISO_DIR_RECORD_S *pstRecord, 
+    OUT BISO_DIR_TREE_S   *pstDirTree 
+);
+VOID BISO_RRIP_GetPXInfo(IN VOID *pEntry, OUT BISO_DIR_TREE_S *pstDirTree);
+VOID BISO_RRIP_GetNMInfo(IN VOID *pEntry, OUT BISO_DIR_TREE_S *pstDirTree);
+VOID BISO_RRIP_GetTFInfo(IN VOID *pEntry, OUT BISO_DIR_TREE_S *pstDirTree);
+VOID BISO_RRIP_GetSLInfo(IN VOID *pEntry, OUT BISO_DIR_TREE_S *pstDirTree);
+VOID BISO_RRIP_GetPNInfo(IN VOID *pEntry, OUT BISO_DIR_TREE_S *pstDirTree);
+
+#endif /* __BISO_ROCKRIDGE_H__ */
+
diff --git a/VtoyTool/BabyISO/biso_util.c b/VtoyTool/BabyISO/biso_util.c
new file mode 100644 (file)
index 0000000..bcf12aa
--- /dev/null
@@ -0,0 +1,626 @@
+/******************************************************************************
+ * biso_util.c
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include "biso.h"
+#include "biso_list.h"
+#include "biso_util.h"
+#include "biso_9660.h"
+
+VOID *zalloc(size_t size)
+{
+    void *p = malloc(size);
+    if (NULL != p)
+    {
+        memset(p, 0, size);
+    }
+    return p;
+}
+
+
+#if (1 == MEMORY_DEBUG)
+STATIC UINT g_uiBISOTotMalloc = 0;
+STATIC UINT g_uiBISOPeekMalloc = 0;
+STATIC UINT g_uiBISOMallocTime = 0;
+STATIC UINT g_uiBISOFreeTime = 0;
+
+VOID *g_apstBISOMalloc[7000];
+VOID *g_apstBISOFree[7000];
+
+VOID * BISO_UTIL_Malloc(IN size_t ulSize)
+{
+    VOID *pData = malloc(ulSize + 4);
+    
+    #if (1 == MEMORY_DEBUG_DUMP)
+    printf("ID %u Malloc %p %lu\n", g_uiBISOMallocTime, (UCHAR *)pData + 4, ulSize);
+    g_apstBISOMalloc[g_uiBISOMallocTime] = (UCHAR *)pData + 4;
+    #endif
+
+    *(UINT32 *)pData = (UINT32)ulSize;
+    g_uiBISOMallocTime++;
+    g_uiBISOTotMalloc += (UINT32)ulSize;
+    if (g_uiBISOTotMalloc > g_uiBISOPeekMalloc)
+    {
+        g_uiBISOPeekMalloc = g_uiBISOTotMalloc;
+    }
+   
+    return (UCHAR *)pData + 4;
+}
+
+VOID *BISO_UTIL_Zalloc(IN size_t ulSize)
+{
+    VOID *pData = zalloc(ulSize + 4);
+
+    #if (1 == MEMORY_DEBUG_DUMP)
+    printf("ID %u Zalloc %p %lu\n", g_uiBISOMallocTime, (UCHAR *)pData + 4, ulSize);
+    g_apstBISOMalloc[g_uiBISOMallocTime] = (UCHAR *)pData + 4;
+    #endif
+
+    *(UINT32 *)pData = (UINT32)ulSize;
+    g_uiBISOMallocTime++;
+    g_uiBISOTotMalloc += (UINT32)ulSize;
+    if (g_uiBISOTotMalloc > g_uiBISOPeekMalloc)
+    {
+        g_uiBISOPeekMalloc = g_uiBISOTotMalloc;
+    }
+   
+    return (UCHAR *)pData + 4;
+}
+
+VOID BISO_UTIL_Free(IN VOID *pData)
+{    
+    #if (1 == MEMORY_DEBUG_DUMP)
+    printf("ID %u Free %p %u\n", g_uiBISOFreeTime, pData, *(UINT32 *)((UCHAR *)pData - 4));
+    g_apstBISOFree[g_uiBISOFreeTime] = pData;
+    #endif
+
+    g_uiBISOFreeTime++;
+    g_uiBISOTotMalloc -= *(UINT32 *)((UCHAR *)pData - 4);
+    if (g_uiBISOTotMalloc > g_uiBISOPeekMalloc)
+    {
+        g_uiBISOPeekMalloc = g_uiBISOTotMalloc;
+    }
+    
+    free((UCHAR *)pData - 4);
+}
+
+VOID BISO_UTIL_DumpMemOp(VOID)
+{
+    BISO_DUMP("\n Memory Operation: Malloc(%u) Free(%u) \nTotal current use %u, Peek memory use %u.\n", 
+              g_uiBISOMallocTime, g_uiBISOFreeTime, g_uiBISOTotMalloc, g_uiBISOPeekMalloc);
+
+#if (1 == MEMORY_DEBUG_DUMP)
+{
+    UINT i, j;
+    for (i = 0; i < g_uiBISOMallocTime; i++)
+    {
+        for (j = 0; j < g_uiBISOFreeTime; j++)
+        {
+            if (g_apstBISOMalloc[i] == g_apstBISOFree[j])
+            {
+                break;
+            }
+        }
+
+        if (j >= g_uiBISOFreeTime)
+        {
+            printf("ID %u ptr %p is not freed.\n", i, g_apstBISOMalloc[i]);
+        }
+    }
+}
+#endif
+
+}
+#endif
+
+INT BISO_UTIL_GetTimeZone(VOID)
+{
+    INT iTimeZone;
+    INT iLocalHour;
+    INT iGMTHour;
+    time_t ulTime;
+    struct tm *pstLocalTM = NULL;
+    struct tm *pstGMTM = NULL;
+    
+    time(&ulTime);
+    pstGMTM = gmtime(&ulTime); 
+    iGMTHour = pstGMTM->tm_hour;
+
+    pstLocalTM = localtime(&ulTime);
+    iLocalHour = pstLocalTM->tm_hour;
+
+    iTimeZone = iLocalHour - iGMTHour;     
+    if (iTimeZone < -12) 
+    {  
+        iTimeZone += 24;
+    } 
+    else if (iTimeZone > 12) 
+    {
+        iTimeZone -= 24;
+    }
+
+    return iTimeZone;
+}
+
+ULONG BISO_UTIL_ReadFile
+(
+    IN  CONST CHAR *pcFileName, 
+    IN  UINT64 ui64Seek, 
+    IN  UINT   uiDataLen,
+    OUT VOID  *pDataBuf
+)
+{
+    UINT uiReadLen = 0;
+    BISO_FILE_S *pstFile = NULL;
+    
+    if ((NULL == pcFileName) || (NULL == pDataBuf))
+    {
+        return BISO_ERR_NULL_PTR;
+    }
+
+    pstFile = BISO_PLAT_OpenExistFile(pcFileName);
+    if (NULL == pstFile)
+    {
+        return BISO_ERR_OPEN_FILE;
+    }
+
+    BISO_PLAT_SeekFile(pstFile, ui64Seek, SEEK_SET);
+    uiReadLen = (UINT)BISO_PLAT_ReadFile(pstFile, 1, uiDataLen, pDataBuf);
+    if (uiReadLen != uiDataLen)
+    {
+        BISO_DIAG("Read Len %u, data len %u.", uiReadLen, uiDataLen);
+        BISO_PLAT_CloseFile(pstFile);
+        return BISO_ERR_READ_FILE;
+    }
+
+    BISO_PLAT_CloseFile(pstFile);
+    return BISO_SUCCESS;
+}
+
+
+CHAR * BISO_UTIL_CopyStr
+(
+    IN  CONST CHAR *szSrc, 
+    IN  UINT        uiSrcSize, 
+    OUT CHAR       *szDest
+)
+{
+    UINT i;
+    UINT uiAllSpace = 1;
+
+    for (i = uiSrcSize; i > 0; i--)
+    {
+        if ((0 != szSrc[i - 1]) && (' ' != szSrc[i - 1]))
+        {
+            uiAllSpace = 0;
+            break;
+        }
+
+        if (' ' != szSrc[i - 1])
+        {
+            uiAllSpace = 0;
+        }
+    }
+
+    if (i > 0)
+    {
+        memcpy(szDest, szSrc, i);
+    }
+    szDest[i] = 0;
+
+    if (uiAllSpace == 1)
+    {
+        scnprintf(szDest, uiSrcSize, "*All Space*"); /* no safe */
+    }
+
+    if (szDest[0] == 0)
+    {
+        scnprintf(szDest, uiSrcSize, "*Empty*"); /* no safe */
+    }
+
+    return szDest;    
+}
+
+CHAR * BISO_UTIL_CopyUCS2Str
+(
+    IN  CONST CHAR *szSrc, 
+    IN  UINT        uiSrcSize, 
+    OUT CHAR       *szDest
+)
+{
+    UINT i;
+    
+    memcpy(szDest, szSrc, uiSrcSize);
+
+    for (i = 0; (i * 2 + 1) < uiSrcSize; i++)
+    {
+        szDest[i] = szDest[i * 2 + 1];
+    }
+    szDest[i] = 0;
+
+    return szDest;    
+}
+
+VOID BISO_UTIL_PathProc(INOUT CHAR *pcPath, INOUT UINT *puiLen)
+{
+    UINT i;
+    
+    if ((NULL == pcPath) || (NULL == puiLen) || (0 == *puiLen))
+    {
+        return;
+    }
+
+    /* 把所有的\替换为/ */
+    for (i = 0; i < *puiLen; i++)
+    {
+        if ('\\' == pcPath[i])
+        {
+            pcPath[i] = '/';
+        }
+    }
+
+    /* 确保最后有1个/ */
+    if ('/' != pcPath[*puiLen - 1])
+    {
+        pcPath[(*puiLen)++] = '/';
+        pcPath[*puiLen] = 0;
+    }
+}
+
+ULONG BISO_UTIL_PathSplit
+(
+    IN CONST CHAR *pcFullPath, 
+    OUT UINT *puiDirNum, 
+    OUT UINT *puiDirPos
+)
+{
+    USHORT usPos = 0;
+    USHORT usLen = 0;
+    UINT uiDirNum = 0;
+    CONST CHAR *pcLastPos = pcFullPath;
+    CONST CHAR *pcCurPos = pcFullPath;
+    
+    DBGASSERT(NULL != pcFullPath);
+    DBGASSERT(NULL != puiDirNum);
+    DBGASSERT(NULL != puiDirPos);
+
+    while (*pcCurPos)
+    {
+        if (('/' == *pcCurPos) || ('\\' == *pcCurPos))
+        {
+            usPos = pcLastPos - pcFullPath;
+            usLen = pcCurPos - pcLastPos;
+            if (usLen <= 0)
+            {
+                return BISO_ERR_FAILED;
+            }
+            
+            puiDirPos[uiDirNum] = (UINT)((UINT)usPos << 16) | usLen;
+
+            uiDirNum++;
+            pcLastPos = pcCurPos + 1;
+        }
+        
+        pcCurPos++;
+    }
+
+    usPos = pcLastPos - pcFullPath;
+    usLen = pcCurPos - pcLastPos;
+    if (usLen <= 0)
+    {
+        return BISO_ERR_FAILED;
+    }    
+    puiDirPos[uiDirNum++] = (UINT)((UINT)usPos << 16) | usLen;
+    
+    *puiDirNum = uiDirNum;
+    return BISO_SUCCESS;
+}
+
+BISO_DIR_TREE_S * BISO_UTIL_FindLinkTgt(IN BISO_DIR_TREE_S *pstCurNode)
+{
+    UINT i = 0;
+    UINT uiDirNum = 0;
+    UINT auiDirPos[32];
+    CHAR szDirName[1024];        
+    USHORT usPos = 0;
+    USHORT usLen = 0;
+    CHAR *pcLink = NULL;
+    BISO_DIR_TREE_S *pstFileList = NULL;
+    BISO_DIR_TREE_S *pstRootDir = NULL;
+    
+    DBGASSERT(NULL != pstCurNode);
+
+    /* 如果不是符号链接则返回自己 */
+    if (BOOL_TRUE != BISO_DIR_TREE_IS_SYMLINK(pstCurNode))
+    {
+        return pstCurNode;
+    }
+
+    pcLink = pstCurNode->pstPosixInfo->pcLinkSrc;
+    
+    if ('/' == pcLink[0])
+    {
+        return NULL;
+    }
+
+    /* 把链接分割开 */
+    if (BISO_SUCCESS != BISO_UTIL_PathSplit(pcLink, &uiDirNum, auiDirPos))
+    {
+        return NULL;
+    }
+
+    pstRootDir = pstCurNode->pstParent;
+
+    /* 依次查找每一部分目录 */
+    for (i = 0; (i < uiDirNum) && (NULL != pstCurNode)&& (NULL != pstRootDir); i++)
+    {
+        usPos = auiDirPos[i] >> 16;
+        usLen = auiDirPos[i] & 0xFF;
+
+        memcpy(szDirName, pcLink + usPos, usLen);
+        szDirName[usLen] = 0;
+
+        if (0 == BISO_PATH_STRCMP(szDirName, "."))
+        {
+            pstCurNode = pstCurNode->pstParent;
+        }
+        else if (0 == BISO_PATH_STRCMP(szDirName, ".."))
+        {
+            if (NULL == pstCurNode->pstParent)
+            {
+                return NULL;
+            }
+            pstCurNode = pstCurNode->pstParent->pstParent;
+            pstRootDir = pstCurNode;
+        }
+        else
+        {
+            pstCurNode = pstRootDir->pstChild;
+            pstFileList = pstRootDir->pstFileList;
+
+            /* 先找当前所在目录下的文件夹 */
+            while (pstCurNode)
+            {
+                if (0 == BISO_PATH_STRCMP(pstCurNode->szName, szDirName))
+                {
+                    pstRootDir = pstCurNode;
+                    break;
+                }
+                pstCurNode = pstCurNode->pstNext;
+            }
+
+            /* 文件夹找不到就找文件 */
+            if (NULL == pstCurNode)
+            {
+                pstCurNode = pstFileList;
+                while (pstCurNode)
+                {
+                    if (0 == BISO_PATH_STRCMP(pstCurNode->szName, szDirName))
+                    {
+                        pstRootDir = NULL;
+                        break;
+                    }
+                    pstCurNode = pstCurNode->pstNext;
+                }
+            }
+        }
+    }
+
+    return pstCurNode;
+}
+
+ULONG BISO_MBUF_Append
+(
+    IN BISO_MBUF_S *pstMBuf, 
+    IN UINT uiDataSize, 
+    IN VOID *pData
+)
+{
+    if ((NULL == pstMBuf) || (pstMBuf->uiCurBufNum >= BISO_MBUF_MAX_BLK))
+    {
+        return BISO_ERR_INVALID_PARAM;
+    }
+
+    pstMBuf->apucDataBuf[pstMBuf->uiCurBufNum] = (UCHAR *)BISO_MALLOC(uiDataSize);
+    if (NULL == pstMBuf->apucDataBuf[pstMBuf->uiCurBufNum])
+    {
+        return BISO_ERR_ALLOC_MEM;
+    }
+
+    if (NULL == pData)
+    {
+        memset(pstMBuf->apucDataBuf[pstMBuf->uiCurBufNum], 0, uiDataSize);        
+    }
+    else
+    {
+        memcpy(pstMBuf->apucDataBuf[pstMBuf->uiCurBufNum], pData, uiDataSize);        
+    }
+    
+    pstMBuf->auiBufSize[pstMBuf->uiCurBufNum] = uiDataSize;
+    pstMBuf->uiTotDataSize += uiDataSize;
+    pstMBuf->uiCurBufNum++;
+    
+    return BISO_SUCCESS;
+}
+
+VOID BISO_MBUF_Free(IN BISO_MBUF_S *pstMBuf)
+{
+    UINT i;
+    if (NULL != pstMBuf)
+    {
+        for (i = 0; i < pstMBuf->uiCurBufNum; i++)
+        {
+            BISO_FREE(pstMBuf->apucDataBuf[i]);
+        }
+        memset(pstMBuf, 0, sizeof(BISO_MBUF_S));
+    }
+}
+
+VOID BISO_MBUF_CopyToBuf(IN CONST BISO_MBUF_S *pstMBuf, OUT VOID *pDataBuf)
+{
+    UINT i;
+    UCHAR *pucDataBuf = (UCHAR *)pDataBuf;
+    
+    if ((NULL != pstMBuf) && (NULL != pucDataBuf))
+    {
+        for (i = 0; i < pstMBuf->uiCurBufNum; i++)
+        {
+            if (NULL != pstMBuf->apucDataBuf[i])
+            {
+                memcpy(pucDataBuf, pstMBuf->apucDataBuf[i], pstMBuf->auiBufSize[i]);
+                pucDataBuf += pstMBuf->auiBufSize[i];
+            }
+        }
+    }
+}
+
+VOID BISO_MBUF_PULLUP(INOUT BISO_MBUF_S *pstMBuf)
+{
+    UINT uiSize = 0;
+    VOID *pData = NULL;
+    
+    DBGASSERT(NULL != pstMBuf);
+
+    if (pstMBuf->uiCurBufNum <= 1)
+    {
+        return;
+    }
+
+    uiSize = pstMBuf->uiTotDataSize;
+    pData = BISO_MALLOC(uiSize);
+    if (NULL == pData)
+    {
+        return;
+    }
+
+    BISO_MBUF_CopyToBuf(pstMBuf, pData);
+    BISO_MBUF_Free(pstMBuf);
+
+    memset(pstMBuf, 0, sizeof(BISO_MBUF_S));
+    pstMBuf->apucDataBuf[0] = (UCHAR *)pData;
+    pstMBuf->auiBufSize[0]  = uiSize;
+    pstMBuf->uiTotDataSize  = uiSize;
+    pstMBuf->uiCurBufNum    = 1;
+
+    return;
+}
+
+BISO_QUEUE_S * BISO_QUEUE_Create(VOID)
+{
+    BISO_DLL_S *pstSLL = (BISO_DLL_S *)BISO_ZALLOC(sizeof(BISO_DLL_S));
+    if (NULL != pstSLL)
+    {
+        BISO_DLL_Init(pstSLL);
+    }
+    return (BISO_QUEUE_S *)pstSLL;
+}
+
+VOID BISO_QUEUE_Destroy(IN BISO_QUEUE_S *pstQueue)
+{
+    BISO_DLL_Free(pstQueue);
+    BISO_FREE(pstQueue);    
+}
+
+VOID BISO_QUEUE_Push(IN BISO_QUEUE_S *pstQueue, IN VOID *pData)
+{
+    BISO_QUEUE_NODE_S *pstNode = NULL;
+    
+    pstNode = (BISO_QUEUE_NODE_S *)BISO_DLL_Last(pstQueue);
+
+    /* 当前节点已满需要扩展新内存节点 */
+    if ((NULL == pstNode) || (BISO_QUEUE_PTR_NUM == pstNode->usLast))
+    {
+        pstNode = (BISO_QUEUE_NODE_S *)BISO_ZALLOC(sizeof(BISO_QUEUE_NODE_S));
+        if (NULL == pstNode)
+        {
+            return;
+        }
+        BISO_DLL_AddTail(pstQueue, (BISO_DLL_NODE_S *)pstNode);
+    }
+
+    /* Last往前走一步 */
+    pstNode = (BISO_QUEUE_NODE_S *)BISO_DLL_Last(pstQueue);
+    pstNode->apList[pstNode->usLast++] = pData;
+}
+
+VOID * BISO_QUEUE_PopHead(IN BISO_QUEUE_S *pstQueue)
+{
+    VOID *pData = NULL;
+    BISO_QUEUE_NODE_S *pstNode = NULL;
+    
+    pstNode = (BISO_QUEUE_NODE_S *)BISO_DLL_First(pstQueue);
+    if (NULL == pstNode)
+    {
+        return NULL;
+    }
+
+    /* First往前走一步 */
+    pData = pstNode->apList[pstNode->usFirst++];
+
+    /* 该节点已空,则摘除节点,释放内存 */
+    if (pstNode->usFirst == pstNode->usLast)
+    {
+        BISO_DLL_DelHead(pstQueue);
+        BISO_FREE(pstNode);
+    }
+
+    return pData;
+}
+
+VOID * BISO_QUEUE_PopTail(IN BISO_QUEUE_S *pstQueue)
+{
+    VOID *pData = NULL;
+    BISO_QUEUE_NODE_S *pstNode = NULL;
+    
+    pstNode = (BISO_QUEUE_NODE_S *)BISO_DLL_Last(pstQueue);
+    if ((NULL == pstNode) || (0 == pstNode->usLast))
+    {
+        return NULL;
+    }
+
+    /* Last往后退一步 */
+    pstNode->usLast--;
+    pData = pstNode->apList[pstNode->usLast];
+
+    /* 该节点已空,则摘除节点,释放内存 */
+    if (pstNode->usFirst == pstNode->usLast)
+    {
+        BISO_DLL_DelTail(pstQueue);
+        BISO_FREE(pstNode);
+    }
+
+    return pData;
+}
+
+UINT64 BISO_UTIL_WholeFile2Buf(IN CONST CHAR *szFileName, OUT UCHAR *pucBuf)
+{
+    UINT uiFileSize;
+    UINT uiReadSize;
+    BISO_FILE_S *pstFile = BISO_PLAT_OpenExistFile(szFileName);
+
+    uiFileSize = BISO_PLAT_GetFileSize(szFileName);
+    uiReadSize = BISO_PLAT_ReadFile(pstFile, 1, uiFileSize, pucBuf);
+
+    BISO_PLAT_CloseFile(pstFile);
+
+    return uiReadSize;
+}
+
+
diff --git a/VtoyTool/BabyISO/biso_util.h b/VtoyTool/BabyISO/biso_util.h
new file mode 100644 (file)
index 0000000..2dbeba1
--- /dev/null
@@ -0,0 +1,250 @@
+/******************************************************************************
+ * biso_util.h
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+#ifndef __BISO_UTIL_H__
+#define __BISO_UTIL_H__
+
+#ifndef scnprintf
+#define scnprintf(buf, bufsize, fmt, arg...) \
+{\
+    snprintf((buf), (bufsize) - 1, fmt, ##arg);\
+    (buf)[(bufsize) - 1] = 0;\
+}
+#endif
+
+#define MEMORY_DEBUG       0
+#define MEMORY_DEBUG_DUMP  0
+
+#if (MEMORY_DEBUG == 1)
+    #define  BISO_MALLOC   BISO_UTIL_Malloc
+    #define  BISO_ZALLOC   BISO_UTIL_Zalloc
+    #define  BISO_FREE     BISO_UTIL_Free
+#else
+    #define  BISO_MALLOC   malloc
+    #define  BISO_ZALLOC   zalloc
+    #define  BISO_FREE     free
+#endif
+
+#define BISO_UCHAR_MAX    0xFF
+#define BISO_USHORT_MAX   0xFFFF
+#define BISO_UINT_MAX     0xFFFFFFFF
+
+/* 无条件字节序转换 */
+#define BISO_SWAP_UINT(data)         \
+    ((((data) & 0x000000FF) << 24) | \
+     (((data) & 0x0000FF00) << 8)  | \
+     (((data) & 0x00FF0000) >> 8)  | \
+     (((data) & 0xFF000000) >> 24))
+
+#define BISO_SWAP_USHORT(data)  \
+    ((USHORT)(((data) & 0x00FF) << 8)  | \
+    (USHORT)(((data) & 0xFF00) >> 8))
+
+/* 更新计数(加或减) */
+#define BISO_STAT_UPDATE(bAdd, a, b) \
+{ \
+    if (BOOL_TRUE == bAdd) \
+    { \
+        (a) += (b); \
+    } \
+    else \
+    { \
+        (a) -= (b); \
+    } \
+}
+
+#if (__BYTE_ORDER == __LITTLE_ENDIAN)
+
+/* 从小字节序转为主机序 */
+#define BISO_LTOH_UINT(data)   data
+#define BISO_LTOH_USHORT(data) data
+
+/* 从主机序转为小字节序 */
+#define BISO_HTOL_UINT(data)   data
+#define BISO_HTOL_USHORT(data) data
+
+/* 从主机序转为大字节序 */
+#define BISO_HTOM_UINT(data)   BISO_SWAP_UINT(data)
+#define BISO_HTOM_USHORT(data) BISO_SWAP_USHORT(data)
+
+#elif (__BYTE_ORDER == __BIG_ENDIAN)
+
+/* 从小字节序转为主机序 */
+#define BISO_LTOH_UINT(data)   BISO_SWAP_UINT(data)
+#define BISO_LTOH_USHORT(data) BISO_SWAP_USHORT(data)
+
+/* 从主机序转为小字节序 */
+#define BISO_HTOL_UINT(data)   BISO_SWAP_UINT(data)
+#define BISO_HTOL_USHORT(data) BISO_SWAP_USHORT(data)
+
+/* 从主机序转为大字节序 */
+#define BISO_HTOM_UINT(data)   data
+#define BISO_HTOM_USHORT(data) data
+
+#else
+#error ("you must first define __BYTE_ORDER !!!")
+#endif
+
+#ifndef ARRAY_SIZE
+#define ARRAY_SIZE(a) ((UINT)(sizeof (a) / sizeof ((a)[0])))
+#endif
+
+#ifndef DBGASSERT
+#define DBGASSERT(expr)
+#endif
+
+/* 读写文件时的缓冲区大小 1M */
+#define BISO_FILE_BUF_LEN   (1024 * 1024)
+
+/* MBUF中最大内存块个数 */
+#define BISO_MBUF_MAX_BLK   256
+
+/* MBUF 结构 */
+typedef struct tagBISO_MBUF
+{
+    UCHAR *apucDataBuf[BISO_MBUF_MAX_BLK];
+    UINT   auiBufSize[BISO_MBUF_MAX_BLK];
+    UINT   uiCurBufNum;
+    UINT   uiTotDataSize;
+}BISO_MBUF_S;
+
+/* 检查读操作句柄有没有已经和ISO文件关联 */
+#define BISO_IS_READ_HANDLE_VALID(pstRead) \
+    (NULL == (((BISO_PARSER_S *)pstRead)->pstPVD) ? BOOL_FALSE : BOOL_TRUE)
+#define BISO_CHECK_READ_HANDLE(pstRead) \
+{\
+    if (NULL == ((BISO_PARSER_S *)(pstRead))->pstPVD) \
+    { \
+        return BISO_ERR_HANDLE_UNINITIALIZED; \
+    } \
+}
+
+/* 设置类型 */
+#define BISO_SET_FLAG(pstFileNode, pstDirTree) \
+{ \
+    (pstFileNode)->ucFlag = BISO_NODE_REGFILE; \
+    if (NULL != (pstDirTree)->pstDirStat) \
+    { \
+        (pstFileNode)->ucFlag = BISO_NODE_DIRECTORY; \
+    } \
+    else if (BOOL_TRUE == BISO_DIR_TREE_IS_SYMLINK(pstDirTree)) \
+    { \
+        (pstFileNode)->ucFlag = BISO_NODE_SYMLINK; \
+    } \
+}
+
+#define BISO_QUEUE_PTR_NUM   1024
+
+/* 队列节点,为避免频繁分配内存,这里每次扩展1024个指针的长度空间 */
+typedef struct tagBISO_QUEUE_NODE
+{
+    BISO_DLL_NODE_S stNode;
+    PVOID apList[BISO_QUEUE_PTR_NUM];
+    USHORT usFirst;
+    USHORT usLast;
+}BISO_QUEUE_NODE_S;
+
+/* 队列/堆栈 简单实现 */
+typedef BISO_DLL_S BISO_QUEUE_S;
+
+INT BISO_UTIL_GetTimeZone(VOID);
+
+CHAR * BISO_UTIL_CopyStr
+(
+    IN CONST CHAR *szSrc,
+    IN UINT uiSrcSize,
+    OUT CHAR *szDest
+);
+CHAR * BISO_UTIL_CopyUCS2Str
+(
+    IN  CONST CHAR *szSrc, 
+    IN  UINT        uiSrcSize, 
+    OUT CHAR       *szDest
+);
+VOID BISO_UTIL_PathProc(INOUT CHAR *pcPath, INOUT UINT *puiLen);
+ULONG BISO_UTIL_PathSplit
+(
+    IN CONST CHAR *pcFullPath, 
+    OUT UINT *puiDirNum, 
+    OUT UINT *puiDirPos
+);
+
+ULONG BISO_UTIL_ReadFile
+(
+    IN  CONST CHAR *pcFileName,
+    IN  UINT64 ui64Seek,
+    IN  UINT   uiDataLen,
+    OUT VOID *pDataBuf
+);
+
+ULONG BISO_MBUF_Append
+(
+    IN BISO_MBUF_S *pstMBuf,
+    IN UINT uiDataSize,
+    IN VOID *pData
+);
+VOID BISO_MBUF_Free(IN BISO_MBUF_S *pstMBuf);
+VOID BISO_MBUF_CopyToBuf(IN CONST BISO_MBUF_S *pstMBuf, OUT VOID *pDataBuf);
+VOID BISO_MBUF_PULLUP(INOUT BISO_MBUF_S *pstMBuf);
+
+BISO_QUEUE_S * BISO_QUEUE_Create(VOID);
+VOID BISO_QUEUE_Destroy(IN BISO_QUEUE_S *pstQueue);
+VOID BISO_QUEUE_Push(IN BISO_QUEUE_S *pstQueue, IN VOID *pData);
+VOID * BISO_QUEUE_PopHead(IN BISO_QUEUE_S *pstQueue);
+VOID * BISO_QUEUE_PopTail(IN BISO_QUEUE_S *pstQueue);
+VOID * BISO_UTIL_Malloc(IN size_t ulSize);
+VOID * BISO_UTIL_Zalloc(IN size_t ulSize);
+VOID BISO_UTIL_Free(IN VOID *pData);
+VOID BISO_UTIL_DumpMemOp(VOID);
+
+ULONG BISO_UTIL_ExtractFile
+(
+    IN BISO_FILE_S *pstFile,
+    IN UINT64  ui64Seek,
+    IN UINT64  ui64Len,
+    IN CONST CHAR *pcNewFileName
+);
+BOOL_T BISO_PLAT_IsPathExist(IN CONST CHAR *pcPath);
+ULONG BISO_PLAT_MkDir(IN CONST CHAR *pcFullPath);
+UINT BISO_PLAT_GetMaxPath(VOID);
+
+BISO_FILE_S * BISO_PLAT_OpenExistFile(IN CONST CHAR *pcFileName);
+BISO_FILE_S * BISO_PLAT_CreateNewFile(IN CONST CHAR *pcFileName);
+VOID BISO_PLAT_CloseFile(IN BISO_FILE_S *pstFile);
+INT64 BISO_PLAT_SeekFile(BISO_FILE_S *pstFile, INT64 i64Offset, INT iFromWhere);
+UINT64 BISO_PLAT_ReadFile
+(
+    IN  BISO_FILE_S *pstFile,
+    IN  UINT         uiBlkSize,
+    IN  UINT         uiBlkNum,
+    OUT VOID        *pBuf
+);
+UINT64 BISO_PLAT_WriteFile
+(
+    IN  BISO_FILE_S *pstFile,
+    IN  UINT         uiBlkSize,
+    IN  UINT         uiBlkNum,
+    IN  VOID        *pBuf
+);
+
+VOID *zalloc(size_t size);
+
+#endif /* __BISO_UTIL_H__ */
+
diff --git a/VtoyTool/build.sh b/VtoyTool/build.sh
new file mode 100644 (file)
index 0000000..07c36be
--- /dev/null
@@ -0,0 +1,20 @@
+#!/bin/bash
+
+rm -f vtoytool/00/*
+
+/opt/diet64/bin/diet -Os gcc -D_FILE_OFFSET_BITS=64  *.c BabyISO/*.c -IBabyISO -Wall -DBUILD_VTOY_TOOL -DUSE_DIET_C  -o  vtoytool_64
+/opt/diet32/bin/diet -Os gcc -D_FILE_OFFSET_BITS=64 -m32  *.c BabyISO/*.c -IBabyISO -Wall -DBUILD_VTOY_TOOL -DUSE_DIET_C  -o  vtoytool_32
+
+#gcc -D_FILE_OFFSET_BITS=64 -static -Wall -DBUILD_VTOY_TOOL  *.c BabyISO/*.c -IBabyISO  -o  vtoytool_64
+#gcc -D_FILE_OFFSET_BITS=64  -Wall -DBUILD_VTOY_TOOL -m32  *.c BabyISO/*.c -IBabyISO  -o  vtoytool_32
+
+if [ -e vtoytool_64 ] && [ -e vtoytool_32 ]; then
+    echo -e '\n############### SUCCESS ###############\n'
+    
+    mv vtoytool_64 vtoytool/00/
+    mv vtoytool_32 vtoytool/00/
+else
+    echo -e '\n############### FAILED ################\n'
+    exit 1
+fi
+
diff --git a/VtoyTool/vtoydm.c b/VtoyTool/vtoydm.c
new file mode 100644 (file)
index 0000000..d45050c
--- /dev/null
@@ -0,0 +1,639 @@
+/******************************************************************************
+ * vtoydm.c  ---- ventoy device mapper tool
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <linux/fs.h>
+#include "biso.h"
+#include "biso_list.h"
+#include "biso_util.h"
+#include "biso_plat.h"
+#include "biso_9660.h"
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+
+#ifndef USE_DIET_C
+typedef unsigned long long uint64_t;
+typedef unsigned int    uint32_t;
+#endif
+
+#pragma pack(4)
+typedef struct ventoy_img_chunk
+{
+    uint32_t img_start_sector; // sector size: 2KB
+    uint32_t img_end_sector;   // included
+
+    uint64_t disk_start_sector; // in disk_sector_size
+    uint64_t disk_end_sector;   // included
+}ventoy_img_chunk;
+#pragma pack()
+
+static int verbose = 0;
+#define debug(fmt, ...) if(verbose) printf(fmt, ##__VA_ARGS__)
+
+#define CMD_PRINT_TABLE       1
+#define CMD_CREATE_DM         2
+#define CMD_DUMP_ISO_INFO     3
+#define CMD_EXTRACT_ISO_FILE  4
+#define CMD_PRINT_EXTRACT_ISO_FILE  5
+
+static uint64_t g_iso_file_size;
+static char g_disk_name[128];
+static int g_img_chunk_num = 0;
+static ventoy_img_chunk *g_img_chunk = NULL;
+static unsigned char g_iso_sector_buf[2048];
+
+ventoy_img_chunk * vtoydm_get_img_map_data(const char *img_map_file, int *plen)
+{
+    int len;
+    int rc = 1;
+    FILE *fp = NULL;
+    ventoy_img_chunk *chunk = NULL;
+    
+    fp = fopen(img_map_file, "rb");
+    if (NULL == fp)
+    {
+        fprintf(stderr, "Failed to open file %s err:%d\n", img_map_file, errno);
+        return NULL;
+    }
+
+    fseek(fp, 0, SEEK_END);
+    len = (int)ftell(fp);
+    fseek(fp, 0, SEEK_SET);
+
+    debug("File <%s> len:%d\n", img_map_file, len);
+
+    chunk = (ventoy_img_chunk *)malloc(len);
+    if (NULL == chunk)
+    {
+        fprintf(stderr, "Failed to malloc memory len:%d err:%d\n", len, errno);
+        goto end;
+    }
+
+    if (fread(chunk, 1, len, fp) != len)
+    {
+        fprintf(stderr, "Failed to read file err:%d\n", errno);
+        goto end;
+    }
+
+    if (len % sizeof(ventoy_img_chunk))
+    {
+        fprintf(stderr, "image map file size %d is not aligned with %d\n", 
+                len, (int)sizeof(ventoy_img_chunk));
+        goto end;
+    }
+
+    rc = 0;
+end:
+    fclose(fp);
+
+    if (rc)
+    {
+        if (chunk)
+        {
+            free(chunk);
+            chunk = NULL;
+        }
+    }
+
+    *plen = len;
+    return chunk;
+}
+
+
+UINT64 vtoydm_get_file_size(const char *pcFileName)
+{
+    (void)pcFileName;
+
+    debug("vtoydm_get_file_size %s %lu\n", pcFileName, (unsigned long)g_iso_file_size);
+    
+    return g_iso_file_size;
+}
+
+BISO_FILE_S * vtoydm_open_file(const char *pcFileName)
+{
+    BISO_FILE_S *file;
+
+    debug("vtoydm_open_file %s\n", pcFileName);
+
+    file = malloc(sizeof(BISO_FILE_S));
+    if (file)
+    {
+        memset(file, 0, sizeof(BISO_FILE_S));
+
+        file->FileSize = g_iso_file_size;
+        file->CurPos = 0;
+    }
+    
+    return file;
+}
+
+void vtoydm_close_file(BISO_FILE_S *pstFile)
+{
+    debug("vtoydm_close_file\n");
+    
+    if (pstFile)
+    {
+        free(pstFile);
+    }
+}
+
+INT64 vtoydm_seek_file(BISO_FILE_S *pstFile, INT64 i64Offset, INT iFromWhere)
+{
+    debug("vtoydm_seek_file %d\n", (int)i64Offset);
+
+    if (iFromWhere == SEEK_SET)
+    {
+        pstFile->CurPos = (UINT64)i64Offset;
+    }
+
+    return 0;
+}
+
+UINT64 vtoydm_map_iso_sector(UINT64 sector)
+{
+    int i;
+    UINT64 disk_sector = 0;
+    
+    for (i = 0; i < g_img_chunk_num; i++)
+    {
+        if (sector >= g_img_chunk[i].img_start_sector && sector <= g_img_chunk[i].img_end_sector)
+        {
+            disk_sector = ((sector - g_img_chunk[i].img_start_sector) << 2) + g_img_chunk[i].disk_start_sector;
+            break;
+        }
+    }
+
+    return disk_sector;
+}
+
+int vtoydm_read_iso_sector(UINT64 sector, void *buf)
+{
+    int i;
+    int fd;
+    UINT64 disk_sector = 0;
+    
+    for (i = 0; i < g_img_chunk_num; i++)
+    {
+        if (sector >= g_img_chunk[i].img_start_sector && sector <= g_img_chunk[i].img_end_sector)
+        {
+            disk_sector = ((sector - g_img_chunk[i].img_start_sector) << 2) + g_img_chunk[i].disk_start_sector;
+            break;
+        }
+    }
+
+    fd = open(g_disk_name, O_RDONLY | O_BINARY);
+    if (fd < 0)
+    {
+        debug("Failed to open %s\n", g_disk_name);
+        return 1;
+    }
+
+    lseek(fd, disk_sector * 512, SEEK_SET);
+
+    read(fd, buf, 2048);
+
+    close(fd);
+    return 0;
+}
+
+UINT64 vtoydm_read_file
+(
+    BISO_FILE_S *pstFile, 
+    UINT         uiBlkSize, 
+    UINT         uiBlkNum, 
+    VOID        *pBuf
+)
+{
+    int pos = 0;
+    int align = 0;
+    UINT64 readlen = uiBlkSize * uiBlkNum;
+    char *curbuf = (char *)pBuf;
+
+    debug("vtoydm_read_file length:%u\n", uiBlkSize * uiBlkNum);
+
+    pos = (int)(pstFile->CurPos % 2048);
+    if (pos > 0)
+    {
+        align = 2048 - pos;
+        
+        vtoydm_read_iso_sector(pstFile->CurPos / 2048, g_iso_sector_buf);
+        if (readlen > align)
+        {
+            memcpy(curbuf, g_iso_sector_buf + pos, align);
+            curbuf  += align;
+            readlen -= align;
+            pstFile->CurPos += align;
+        }
+        else
+        {
+            memcpy(curbuf, g_iso_sector_buf + pos, readlen);
+            pstFile->CurPos += readlen;
+            return readlen;
+        }
+    }
+
+    while (readlen > 2048)
+    {
+        vtoydm_read_iso_sector(pstFile->CurPos / 2048, curbuf);
+        pstFile->CurPos += 2048;
+        
+        curbuf += 2048;
+        readlen -= 2048;
+    }
+
+    if (readlen > 0)
+    {
+        vtoydm_read_iso_sector(pstFile->CurPos / 2048, g_iso_sector_buf);
+        memcpy(curbuf, g_iso_sector_buf, readlen);
+        pstFile->CurPos += readlen;
+    }
+    
+    return uiBlkSize * uiBlkNum;
+}
+
+int vtoydm_dump_iso(const char *img_map_file, const char *diskname)
+{
+    int i = 0;
+    int len = 0;
+    uint64_t sector_num;
+    unsigned long ret;
+    ventoy_img_chunk *chunk = NULL;
+    BISO_READ_S *iso;
+    BISO_PARSER_S *parser = NULL;
+    char label[64] = {0};
+    
+    chunk = vtoydm_get_img_map_data(img_map_file, &len);
+    if (NULL == chunk)
+    {
+        return 1;
+    }
+
+    for (i = 0; i < len / sizeof(ventoy_img_chunk); i++)
+    {
+        sector_num = chunk[i].img_end_sector - chunk[i].img_start_sector + 1;
+        g_iso_file_size += sector_num * 2048;
+    }
+
+    strncpy(g_disk_name, diskname, sizeof(g_disk_name) - 1);
+    g_img_chunk = chunk;
+    g_img_chunk_num = len / sizeof(ventoy_img_chunk);
+
+    debug("iso file size : %llu\n", (unsigned long long)g_iso_file_size);
+
+    iso = BISO_AllocReadHandle();
+    if (iso == NULL)
+    {
+        free(chunk);
+        return 1;
+    }
+
+    ret = BISO_OpenImage("XXX", iso);
+    debug("open iso image ret=0x%lx\n", ret);
+
+    parser = (BISO_PARSER_S *)iso;    
+    memcpy(label, parser->pstPVD->szVolumeId, 32);
+    for (i = 32; i >=0; i--)
+    {
+        if (label[i] != 0 && label[i] != ' ')
+        {
+            break;
+        }
+        else
+        {
+            label[i] = 0;
+        }
+    }
+
+    if (label[0])
+    {
+        printf("VENTOY_ISO_LABEL %s\n", label);    
+    }
+    
+    BISO_DumpFileTree(iso);
+    
+    BISO_FreeReadHandle(iso);
+
+    free(chunk);
+    return 0;
+}
+
+static int vtoydm_extract_iso
+(
+    const char *img_map_file, 
+    const char *diskname,
+    unsigned long first_sector,
+    unsigned long long file_size,
+    const char *outfile
+)
+{
+    int len;
+    FILE *fp = NULL;
+
+    g_img_chunk = vtoydm_get_img_map_data(img_map_file, &len);
+    if (NULL == g_img_chunk)
+    {
+        return 1;
+    }
+
+    strncpy(g_disk_name, diskname, sizeof(g_disk_name) - 1);
+    g_img_chunk_num = len / sizeof(ventoy_img_chunk);
+
+    fp = fopen(outfile, "wb");
+    if (fp == NULL)
+    {
+        fprintf(stderr, "Failed to create file %s err:%d\n", outfile, errno);
+        free(g_img_chunk);
+        return 1;
+    }
+
+    while (file_size > 0)
+    {
+        vtoydm_read_iso_sector(first_sector++, g_iso_sector_buf);
+        if (file_size > 2048)
+        {
+            fwrite(g_iso_sector_buf, 2048, 1, fp);
+            file_size -= 2048;
+        }
+        else
+        {
+            fwrite(g_iso_sector_buf, 1, file_size, fp);
+            file_size = 0;
+        }
+    }
+    
+    fclose(fp);
+    free(g_img_chunk);
+    return 0;
+}
+
+
+static int vtoydm_print_extract_iso
+(
+    const char *img_map_file, 
+    const char *diskname,
+    unsigned long first_sector,
+    unsigned long long file_size,
+    const char *outfile
+)
+{
+    int len;
+    uint32_t last = 0;
+    uint32_t sector = 0;
+    uint32_t disk_first = 0;
+    uint32_t count = 0;
+    uint32_t buf[2];
+    uint64_t size = file_size;
+    FILE *fp = NULL;
+
+    g_img_chunk = vtoydm_get_img_map_data(img_map_file, &len);
+    if (NULL == g_img_chunk)
+    {
+        return 1;
+    }
+
+    strncpy(g_disk_name, diskname, sizeof(g_disk_name) - 1);
+    g_img_chunk_num = len / sizeof(ventoy_img_chunk);
+
+    fp = fopen(outfile, "wb");
+    if (fp == NULL)
+    {
+        fprintf(stderr, "Failed to create file %s err:%d\n", outfile, errno);
+        free(g_img_chunk);
+        return 1;
+    }
+
+    fwrite(g_disk_name, 1, 32, fp);
+    fwrite(&size, 1, 8, fp);
+
+    while (file_size > 0)
+    {
+        sector = vtoydm_map_iso_sector(first_sector++);
+
+        if (count > 0 && sector == last + 4)
+        {
+            last += 4;
+            count += 4;
+        }
+        else
+        {
+            if (count > 0)
+            {
+                buf[0] = disk_first;
+                buf[1] = count;
+                fwrite(buf, 1, sizeof(buf), fp);
+            }
+
+            disk_first = sector;
+            last = sector;
+            count = 4;
+        }
+        
+        if (file_size > 2048)
+        {
+            file_size -= 2048;
+        }
+        else
+        {
+            file_size = 0;
+        }
+    }
+
+    if (count > 0)
+    {
+        buf[0] = disk_first;
+        buf[1] = count;
+        fwrite(buf, 1, sizeof(buf), fp);
+    }
+    
+    fclose(fp);
+    free(g_img_chunk);
+    return 0;
+}
+
+
+
+
+static int vtoydm_print_linear_table(const char *img_map_file, const char *diskname)
+{
+    int i;
+    int len;
+    uint32_t sector_start;
+    uint32_t sector_num;
+    ventoy_img_chunk *chunk = NULL;
+    
+    chunk = vtoydm_get_img_map_data(img_map_file, &len);
+    if (NULL == chunk)
+    {
+        return 1;
+    }
+
+    for (i = 0; i < len / sizeof(ventoy_img_chunk); i++)
+    {
+        sector_start = chunk[i].img_start_sector;
+        sector_num = chunk[i].img_end_sector - chunk[i].img_start_sector + 1;
+        
+        printf("%u %u linear %s %llu\n", 
+               (sector_start << 2), (sector_num << 2), 
+               diskname, (unsigned long long)chunk[i].disk_start_sector);
+    }
+
+    free(chunk);
+    return 0;
+}
+
+static int vtoydm_print_help(FILE *fp)
+{
+    fprintf(fp, "Usage: \n"
+            "   vtoydm -p -f img_map_file -d diskname [ -v ] \n"
+            "   vtoydm -c -f img_map_file -d diskname [ -v ] \n"
+            "   vtoydm -i -f img_map_file -d diskname [ -v ] \n"
+            "   vtoydm -e -f img_map_file -d diskname -s sector -l len -o file [ -v ] \n"
+            );
+    return 0;        
+}
+
+int vtoydm_main(int argc, char **argv)
+{
+    int ch;
+    int cmd = 0;
+    unsigned long first_sector = 0;
+    unsigned long long file_size = 0;
+    char diskname[128] = {0};
+    char filepath[300] = {0};
+    char outfile[300] = {0};
+
+    while ((ch = getopt(argc, argv, "s:l:o:d:f:v::i::p::c::h::e::E::")) != -1)
+    {
+        if (ch == 'd')
+        {
+            strncpy(diskname, optarg, sizeof(diskname) - 1);
+        }
+        else if (ch == 'f')
+        {
+            strncpy(filepath, optarg, sizeof(filepath) - 1);
+        }
+        else if (ch == 'p')
+        {
+            cmd = CMD_PRINT_TABLE;
+        }
+        else if (ch == 'c')
+        {
+            cmd = CMD_CREATE_DM;
+        }
+        else if (ch == 'i')
+        {
+            cmd = CMD_DUMP_ISO_INFO;
+        }
+        else if (ch == 'e')
+        {
+            cmd = CMD_EXTRACT_ISO_FILE;
+        }
+        else if (ch == 'E')
+        {
+            cmd = CMD_PRINT_EXTRACT_ISO_FILE;
+        }
+        else if (ch == 's')
+        {
+            first_sector = strtoul(optarg, NULL, 10);
+        }
+        else if (ch == 'l')
+        {
+            file_size = strtoull(optarg, NULL, 10);
+        }
+        else if (ch == 'o')
+        {
+            strncpy(outfile, optarg, sizeof(outfile) - 1);
+        }
+        else if (ch == 'v')
+        {
+            verbose = 1;
+        }
+        else if (ch == 'h')
+        {
+            return vtoydm_print_help(stdout);
+        }
+        else
+        {
+            vtoydm_print_help(stderr);
+            return 1;
+        }
+    }
+
+    if (filepath[0] == 0 || diskname[0] == 0)
+    {
+        fprintf(stderr, "Must input file and disk\n");
+        return 1;
+    }
+
+    debug("cmd=%d file=<%s> disk=<%s> first_sector=%lu file_size=%llu\n", 
+          cmd, filepath, diskname, first_sector, file_size);
+
+    switch (cmd)
+    {
+        case CMD_PRINT_TABLE:
+        {
+            return vtoydm_print_linear_table(filepath, diskname);
+        }
+        case CMD_CREATE_DM:
+        {
+            break;
+        }
+        case CMD_DUMP_ISO_INFO:
+        {
+            return vtoydm_dump_iso(filepath, diskname);
+        }
+        case CMD_EXTRACT_ISO_FILE:
+        {
+            return vtoydm_extract_iso(filepath, diskname, first_sector, file_size, outfile);
+        }
+        case CMD_PRINT_EXTRACT_ISO_FILE:
+        {
+            return vtoydm_print_extract_iso(filepath, diskname, first_sector, file_size, outfile);
+        }
+        default :
+        {
+            fprintf(stderr, "Invalid cmd \n");
+            return 1;
+        }
+    }
+
+       return 0;
+}
+
+// wrapper main
+#ifndef BUILD_VTOY_TOOL
+int main(int argc, char **argv)
+{
+    return vtoydm_main(argc, argv);
+}
+#endif
+
diff --git a/VtoyTool/vtoydump.c b/VtoyTool/vtoydump.c
new file mode 100644 (file)
index 0000000..003fd1a
--- /dev/null
@@ -0,0 +1,536 @@
+/******************************************************************************
+ * vtoydump.c  ---- Dump ventoy os parameters 
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <linux/fs.h>
+#include <dirent.h>
+
+#define IS_DIGIT(x) ((x) >= '0' && (x) <= '9')
+
+#ifndef USE_DIET_C
+typedef unsigned long long uint64_t;
+typedef unsigned int    uint32_t;
+typedef unsigned short  uint16_t;
+typedef unsigned char   uint8_t;
+#endif
+
+#define VENTOY_GUID { 0x77772020, 0x2e77, 0x6576, { 0x6e, 0x74, 0x6f, 0x79, 0x2e, 0x6e, 0x65, 0x74 }}
+
+#pragma pack(1)
+
+typedef struct ventoy_guid
+{
+    uint32_t   data1;
+    uint16_t   data2;
+    uint16_t   data3;
+    uint8_t    data4[8];
+}ventoy_guid;
+
+
+typedef struct ventoy_image_disk_region
+{
+    uint32_t   image_sector_count; /* image sectors contained in this region */
+    uint32_t   image_start_sector; /* image sector start */
+    uint64_t   disk_start_sector;  /* disk sector start */
+}ventoy_image_disk_region;
+
+typedef struct ventoy_image_location
+{
+    ventoy_guid  guid;
+    
+    /* image sector size, currently this value is always 2048 */
+    uint32_t   image_sector_size;
+
+    /* disk sector size, normally the value is 512 */
+    uint32_t   disk_sector_size;
+
+    uint32_t   region_count;
+    
+    /*
+     * disk region data
+     * If the image file has more than one fragments in disk, 
+     * there will be more than one region data here.
+     * You can calculate the region count by 
+     */
+    ventoy_image_disk_region regions[1];
+
+    /* ventoy_image_disk_region regions[2~region_count-1] */
+}ventoy_image_location;
+
+typedef struct ventoy_os_param
+{
+    ventoy_guid    guid;             // VENTOY_GUID
+    uint8_t        chksum;           // checksum
+
+    uint8_t   vtoy_disk_guid[16];
+    uint64_t  vtoy_disk_size;       // disk size in bytes
+    uint16_t  vtoy_disk_part_id;    // begin with 1
+    uint16_t  vtoy_disk_part_type;  // 0:exfat   1:ntfs  other: reserved
+    char      vtoy_img_path[384];   // It seems to be enough, utf-8 format
+    uint64_t  vtoy_img_size;        // image file size in bytes
+
+    /* 
+     * Ventoy will write a copy of ventoy_image_location data into runtime memory
+     * this is the physically address and length of that memory.
+     * Address 0 means no such data exist.
+     * Address will be aligned by 4KB.
+     *
+     */
+    uint64_t  vtoy_img_location_addr;
+    uint32_t  vtoy_img_location_len;
+
+    uint64_t  vtoy_reserved[4];     // Internal use by ventoy
+
+    uint8_t   reserved[31];
+}ventoy_os_param;
+
+#pragma pack()
+
+#ifndef O_BINARY
+#define O_BINARY 0
+#endif
+#if defined(_dragon_fly) || defined(_free_BSD) || defined(_QNX)
+#define MMAP_FLAGS          MAP_SHARED
+#else
+#define MMAP_FLAGS          MAP_PRIVATE
+#endif
+
+#define SEARCH_MEM_START 0x80000
+#define SEARCH_MEM_LEN   0x1c000
+
+static int verbose = 0;
+#define debug(fmt, ...) if(verbose) printf(fmt, ##__VA_ARGS__)
+
+static ventoy_guid vtoy_guid = VENTOY_GUID;
+
+static int vtoy_check_os_param(ventoy_os_param *param)
+{
+    uint32_t i;
+    uint8_t  chksum = 0;
+    uint8_t *buf = (uint8_t *)param;
+    
+    if (memcmp(&param->guid, &vtoy_guid, sizeof(ventoy_guid)))
+    {
+        return 1;
+    }
+
+    for (i = 0; i < sizeof(ventoy_os_param); i++)
+    {
+        chksum += buf[i];
+    }
+
+    if (chksum)
+    {
+        debug("Invalid checksum 0x%02x\n", chksum);
+        return 1;
+    }
+
+    return 0;
+}
+
+static int vtoy_os_param_from_file(const char *filename, ventoy_os_param *param)
+{
+    int fd = 0;
+    int rc = 0;
+
+    fd = open(filename, O_RDONLY | O_BINARY);
+    if (fd < 0)
+    {
+        fprintf(stderr, "Failed to open file %s error %d\n", filename, errno);
+        return errno;
+    }
+
+    read(fd, param, sizeof(ventoy_os_param));
+
+    if (vtoy_check_os_param(param) == 0)
+    {
+        debug("find ventoy os param in file %s\n", filename);
+    }
+    else
+    {
+        debug("ventoy os pararm NOT found in file %s\n", filename);
+        rc = 1;
+    }
+    
+    close(fd);
+    return rc;
+}
+
+static void vtoy_dump_os_param(ventoy_os_param *param)
+{
+    printf("################# dump os param ################\n");
+
+    printf("param->chksum = 0x%x\n", param->chksum);
+    printf("param->vtoy_disk_guid = %02x %02x %02x %02x\n", 
+        param->vtoy_disk_guid[0], param->vtoy_disk_guid[1], 
+        param->vtoy_disk_guid[2], param->vtoy_disk_guid[3]);
+    printf("param->vtoy_disk_size = %llu\n", (unsigned long long)param->vtoy_disk_size);
+    printf("param->vtoy_disk_part_id = %u\n", param->vtoy_disk_part_id);
+    printf("param->vtoy_disk_part_type = %u\n", param->vtoy_disk_part_type);
+    printf("param->vtoy_img_path = <%s>\n", param->vtoy_img_path);
+    printf("param->vtoy_img_size = <%llu>\n", (unsigned long long)param->vtoy_img_size);
+    printf("param->vtoy_img_location_addr = <0x%llx>\n", (unsigned long long)param->vtoy_img_location_addr);
+    printf("param->vtoy_img_location_len = <%u>\n", param->vtoy_img_location_len);
+    printf("param->vtoy_reserved[0] = 0x%llx\n", (unsigned long long)param->vtoy_reserved[0]);
+    printf("param->vtoy_reserved[1] = 0x%llx\n", (unsigned long long)param->vtoy_reserved[1]);
+    
+    printf("\n");
+}
+
+static int vtoy_get_disk_guid(const char *diskname, uint8_t *vtguid)
+{
+    int i = 0;
+    int fd = 0;
+    char devdisk[128] = {0};
+
+    snprintf(devdisk, sizeof(devdisk) - 1, "/dev/%s", diskname);
+    
+    fd = open(devdisk, O_RDONLY | O_BINARY);
+    if (fd >= 0)
+    {
+        lseek(fd, 0x180, SEEK_SET);
+        read(fd, vtguid, 16);
+        close(fd);
+
+        debug("GUID for %s: <", devdisk);
+        for (i = 0; i < 16; i++)
+        {
+            debug("%02x", vtguid[i]);
+        }
+        debug(">\n");
+        
+        return 0;
+    }
+    else
+    {
+        debug("failed to open %s %d\n", devdisk, errno);
+        return errno;
+    }
+}
+
+static unsigned long long vtoy_get_disk_size_in_byte(const char *disk)
+{
+    int fd;
+    int rc;
+    unsigned long long size = 0;
+    char diskpath[256] = {0};
+    char sizebuf[64] = {0};
+
+    // Try 1: get size from sysfs
+    snprintf(diskpath, sizeof(diskpath) - 1, "/sys/block/%s/size", disk);
+    if (access(diskpath, F_OK) >= 0)
+    {
+        debug("get disk size from sysfs for %s\n", disk);
+        
+        fd = open(diskpath, O_RDONLY | O_BINARY);
+        if (fd >= 0)
+        {
+            read(fd, sizebuf, sizeof(sizebuf));
+            size = strtoull(sizebuf, NULL, 10);
+            close(fd);
+            return (size * 512);
+        }
+    }
+    else
+    {
+        debug("%s not exist \n", diskpath);
+    }
+
+    // Try 2: get size from ioctl
+    snprintf(diskpath, sizeof(diskpath) - 1, "/dev/%s", disk);
+    fd = open(diskpath, O_RDONLY);
+    if (fd >= 0)
+    {
+        debug("get disk size from ioctl for %s\n", disk);
+        rc = ioctl(fd, BLKGETSIZE64, &size);
+        if (rc == -1)
+        {
+            size = 0;
+            debug("failed to ioctl %d\n", rc);
+        }
+        close(fd);
+    }
+    else
+    {
+        debug("failed to open %s %d\n", diskpath, errno);
+    }
+
+    debug("disk %s size %llu bytes\n", disk, (unsigned long long)size);
+    return size;
+}
+
+static int vtoy_is_possible_blkdev(const char *name)
+{
+    if (name[0] == '.')
+    {
+        return 0;
+    }
+
+    /* /dev/ramX */
+    if (name[0] == 'r' && name[1] == 'a' && name[2] == 'm')
+    {
+        return 0;
+    }
+
+    /* /dev/loopX */
+    if (name[0] == 'l' && name[1] == 'o' && name[2] == 'o' && name[3] == 'p')
+    {
+        return 0;
+    }
+
+    /* /dev/dm-X */
+    if (name[0] == 'd' && name[1] == 'm' && name[2] == '-' && IS_DIGIT(name[3]))
+    {
+        return 0;
+    }
+
+    /* /dev/srX */
+    if (name[0] == 's' && name[1] == 'r' && IS_DIGIT(name[2]))
+    {
+        return 0;
+    }
+    
+    return 1;
+}
+
+static int vtoy_find_disk_by_size(unsigned long long size, char *diskname)
+{
+    unsigned long long cursize = 0;
+    DIR* dir = NULL;
+    struct dirent* p = NULL;
+    int rc = 0;
+
+    dir = opendir("/sys/block");
+    while ((p = readdir(dir)) != NULL)
+    {
+        if (!vtoy_is_possible_blkdev(p->d_name))
+        {
+            debug("disk %s is filted by name\n", p->d_name);
+            continue;
+        }
+    
+        cursize = vtoy_get_disk_size_in_byte(p->d_name);
+        debug("disk %s size %llu\n", p->d_name, (unsigned long long)cursize);
+        if (cursize == size)
+        {
+            sprintf(diskname, "%s", p->d_name);
+            rc++;
+        }
+    }
+    closedir(dir);
+    return rc;    
+}
+
+static int vtoy_find_disk_by_guid(uint8_t *guid, char *diskname)
+{
+    int rc = 0;
+    int count = 0;
+    DIR* dir = NULL;
+    struct dirent* p = NULL;
+    uint8_t vtguid[16];
+
+    dir = opendir("/sys/block");
+    while ((p = readdir(dir)) != NULL)
+    {
+        if (!vtoy_is_possible_blkdev(p->d_name))
+        {
+            debug("disk %s is filted by name\n", p->d_name);        
+            continue;
+        }
+    
+        memset(vtguid, 0, sizeof(vtguid));
+        rc = vtoy_get_disk_guid(p->d_name, vtguid);
+        if (rc == 0 && memcmp(vtguid, guid, 16) == 0)
+        {
+            sprintf(diskname, "%s", p->d_name);
+            count++;
+        }
+    }
+    closedir(dir);
+    
+    return count;    
+}
+
+static int vtoy_print_os_param(ventoy_os_param *param, char *diskname)
+{
+    int   cnt = 0;
+    char *path = param->vtoy_img_path;
+    const char *fs;
+
+    cnt = vtoy_find_disk_by_size(param->vtoy_disk_size, diskname);
+    if (cnt > 1)
+    {
+        cnt = vtoy_find_disk_by_guid(param->vtoy_disk_guid, diskname);
+    }
+    
+    if (param->vtoy_disk_part_type == 0)
+    {
+        fs = "exfat";
+    }
+    else if (param->vtoy_disk_part_type == 0)
+    {
+        fs = "ntfs";
+    }
+    else
+    {
+        fs = "unknown";
+    }
+
+    if (1 == cnt)
+    {
+        printf("/dev/%s#%s#%s\n", diskname, fs, path);
+        return 0;
+    }
+    else
+    {
+        return 1;
+    }
+}
+
+static int vtoy_check_device(ventoy_os_param *param, const char *device)
+{
+    unsigned long long size; 
+    uint8_t vtguid[16] = {0};
+
+    debug("vtoy_check_device for <%s>\n", device);
+
+    size = vtoy_get_disk_size_in_byte(device);
+    vtoy_get_disk_guid(device, vtguid);
+
+    debug("param->vtoy_disk_size=%llu size=%llu\n", 
+        (unsigned long long)param->vtoy_disk_size, (unsigned long long)size);
+
+    if (param->vtoy_disk_size == size && 
+        memcmp(vtguid, param->vtoy_disk_guid, 16) == 0)
+    {
+        debug("<%s> is right ventoy disk\n", device);
+        return 0;
+    }
+    else
+    {
+        debug("<%s> is NOT right ventoy disk\n", device);
+        return 1;
+    }
+}
+
+/*
+ *  Find disk and image path from ventoy runtime data.
+ *  By default data is read from phymem(legacy bios) or efivar(UEFI), if -f is input, data is read from file.
+ *  
+ *  -f datafile     os param data file. 
+ *  -c /dev/xxx     check ventoy disk
+ *  -v              be verbose
+ *  -l              also print image disk location 
+ */
+int vtoydump_main(int argc, char **argv)
+{
+    int rc;
+    int ch;
+    char filename[256] = {0};
+    char diskname[256] = {0};
+    char device[64] = {0};
+    ventoy_os_param *param = NULL;
+
+    while ((ch = getopt(argc, argv, "c:f:v::")) != -1)
+    {
+        if (ch == 'f')
+        {
+            strncpy(filename, optarg, sizeof(filename) - 1);
+        }
+        else if (ch == 'v')
+        {
+            verbose = 1;
+        }
+        else if (ch == 'c')
+        {
+            strncpy(device, optarg, sizeof(device) - 1);
+        }
+        else
+        {
+            fprintf(stderr, "Usage: %s -f datafile [ -v ] \n", argv[0]);
+            return 1;
+        }
+    }
+
+    if (filename[0] == 0)
+    {
+        fprintf(stderr, "Usage: %s -f datafile [ -v ] \n", argv[0]);
+        return 1;
+    }
+
+    param = malloc(sizeof(ventoy_os_param));
+    if (NULL == param)
+    {
+        fprintf(stderr, "failed to alloc memory with size %d error %d\n", 
+                (int)sizeof(ventoy_os_param), errno);
+        return 1;
+    }
+    
+    memset(param, 0, sizeof(ventoy_os_param));
+
+    debug("get os pararm from file %s\n", filename);
+    rc = vtoy_os_param_from_file(filename, param);
+    if (rc)
+    {
+        debug("ventoy os param not found %d\n", rc);
+        goto end;
+    }
+
+    if (verbose)
+    {
+        vtoy_dump_os_param(param);
+    }
+
+    if (device[0])
+    {
+        rc = vtoy_check_device(param, device);
+    }
+    else
+    {
+        // print os param, you can change the output format in the function
+        rc = vtoy_print_os_param(param, diskname);
+    }
+
+end:
+    if (param)
+    {
+        free(param);
+    }
+    return rc;
+}
+
+// wrapper main
+#ifndef BUILD_VTOY_TOOL
+int main(int argc, char **argv)
+{
+    return vtoydump_main(argc, argv);
+}
+#endif
+
diff --git a/VtoyTool/vtoyloader.c b/VtoyTool/vtoyloader.c
new file mode 100644 (file)
index 0000000..0272d7f
--- /dev/null
@@ -0,0 +1,172 @@
+/******************************************************************************
+ * vtoyloader.c  ---- ventoy loader (wapper for binary loader)
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/mman.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#define MAX_EXT_PARAM       256
+#define CMDLINE_BUF_LEN     (1024 * 1024 * 1)
+#define EXEC_PATH_FILE      "/ventoy/loader_exec_file"
+#define CMDLINE_FILE        "/ventoy/loader_exec_cmdline"
+#define HOOK_CMD_FILE       "/ventoy/loader_hook_cmd"
+#define DEBUG_FLAG_FILE     "/ventoy/loader_debug"
+
+static int verbose = 0;
+#define debug(fmt, ...) if(verbose) printf(fmt, ##__VA_ARGS__)
+
+static char g_exec_file[512];
+static char g_hook_cmd[512];
+
+static int vtoy_read_file_to_buf(const char *file, void *buf, int buflen)
+{
+    FILE *fp;
+
+    fp = fopen(file, "r");
+    if (!fp)
+    {
+        fprintf(stderr, "Failed to open file %s err:%d\n", file, errno);
+        return 1;
+    }
+    fread(buf, 1, buflen, fp);
+    fclose(fp);
+
+    return 0;
+}
+
+int vtoyloader_main(int argc, char **argv)
+{
+    int i;
+    int len;
+    int rc;
+    char *pos;
+    char *cmdline;
+    char **cmdlist;
+
+    if (access(DEBUG_FLAG_FILE, F_OK) >= 0)
+    {
+        verbose = 1;
+    }
+
+    debug("ventoy loader ...\n");
+    
+    rc = vtoy_read_file_to_buf(EXEC_PATH_FILE, g_exec_file, sizeof(g_exec_file) - 1);
+    if (rc)
+    {
+        return rc;
+    }
+
+    if (access(g_exec_file, F_OK) < 0)
+    {
+        fprintf(stderr, "File %s not exist\n", g_exec_file);
+        return 1;
+    }
+
+    if (access(HOOK_CMD_FILE, F_OK) >= 0)
+    {
+        rc = vtoy_read_file_to_buf(HOOK_CMD_FILE,  g_hook_cmd,  sizeof(g_hook_cmd) - 1);
+        debug("g_hook_cmd=<%s>\n", g_hook_cmd);
+
+        // call hook script
+        rc = system(g_hook_cmd);
+        debug("system return code =<%d>  errno=<%d>\n", rc, errno);
+    }
+
+    cmdline = (char *)malloc(CMDLINE_BUF_LEN);
+    if (!cmdline)
+    {
+        fprintf(stderr, "Failed to alloc memory err:%d\n", errno);
+        return 1;
+    }
+    memset(cmdline, 0, CMDLINE_BUF_LEN);
+
+    if (access(CMDLINE_FILE, F_OK) >= 0)
+    {
+        rc = vtoy_read_file_to_buf(CMDLINE_FILE, cmdline, CMDLINE_BUF_LEN - 1);
+        if (rc)
+        {
+            return rc;
+        }
+    }
+
+    len = (int)((argc + MAX_EXT_PARAM) * sizeof(char *));
+    cmdlist = (char **)malloc(len);
+    if (!cmdlist)
+    {
+        free(cmdline);
+        fprintf(stderr, "Failed to alloc memory err:%d\n", errno);
+        return 1;
+    }
+    memset(cmdlist, 0, len);
+
+    for (i = 0; i < argc; i++)
+    {
+        cmdlist[i] = argv[i];
+    }
+    
+    cmdlist[0] = g_exec_file;
+    debug("g_exec_file=<%s>\n", g_exec_file);
+
+    pos = cmdline;
+    while ((*pos) && i < MAX_EXT_PARAM)
+    {
+        cmdlist[i++] = pos;
+
+        while (*pos)
+        {
+            if (*pos == '\r')
+            {
+                *pos = ' ';
+            }
+            else if (*pos == '\n')
+            {
+                *pos = 0;
+                pos++;
+                break;
+            }
+        
+            pos++;
+        }
+    }
+
+    debug("execv [%s]...\n", cmdlist[0]);
+
+    execv(cmdlist[0], cmdlist);
+    
+    return 0;
+}
+
+
+// wrapper main
+#ifndef BUILD_VTOY_TOOL
+int main(int argc, char **argv)
+{
+    return vtoyloader_main(argc, argv);
+}
+#endif
+
diff --git a/VtoyTool/vtoytool.c b/VtoyTool/vtoytool.c
new file mode 100644 (file)
index 0000000..f4237f5
--- /dev/null
@@ -0,0 +1,95 @@
+/******************************************************************************
+ * vtoytool.c  ---- ventoy os tool
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+typedef int (*main_func)(int argc, char **argv);
+
+typedef struct cmd_def
+{
+    const char *cmd;
+    main_func func;
+}cmd_def;
+
+int vtoydump_main(int argc, char **argv);
+int vtoydm_main(int argc, char **argv);
+int vtoytool_install(int argc, char **argv);
+int vtoyloader_main(int argc, char **argv);
+
+static char *g_vtoytool_name = NULL;
+static cmd_def g_cmd_list[] = 
+{
+    { "vtoydump",    vtoydump_main    },
+    { "vtoydm",      vtoydm_main      },
+    { "loader",      vtoyloader_main  },
+    { "--install",   vtoytool_install },
+};
+
+
+int vtoytool_install(int argc, char **argv)
+{
+    int i;
+    char toolpath[128];
+    char filepath[128];
+    
+    for (i = 0; i < sizeof(g_cmd_list) / sizeof(g_cmd_list[0]); i++)
+    {
+        if (g_cmd_list[i].cmd[0] != '-')
+        {
+            snprintf(toolpath, sizeof(toolpath), "/ventoy/tool/%s", g_vtoytool_name);
+            snprintf(filepath, sizeof(filepath), "/ventoy/tool/%s", g_cmd_list[i].cmd);
+            link(toolpath, filepath);
+        }
+    }
+    
+    return 0;
+}
+
+int main(int argc, char **argv)
+{
+    int i;
+    
+    if ((g_vtoytool_name = strstr(argv[0], "vtoytool")) != NULL)
+    {
+        argc--;
+        argv++;
+    }
+
+    if (argc == 0)
+    {
+        fprintf(stderr, "Invalid param number\n");
+        return 1;
+    }
+
+    for (i = 0; i < sizeof(g_cmd_list) / sizeof(g_cmd_list[0]); i++)
+    {
+        if (strstr(argv[0], g_cmd_list[i].cmd))
+        {
+            return g_cmd_list[i].func(argc, argv);
+        }
+    }
+
+    fprintf(stderr, "Invalid cmd %s\n", argv[0]);
+    return 1;
+}
+
diff --git a/VtoyTool/vtoytool/00/vtoytool_32 b/VtoyTool/vtoytool/00/vtoytool_32
new file mode 100644 (file)
index 0000000..ebc99f8
Binary files /dev/null and b/VtoyTool/vtoytool/00/vtoytool_32 differ
diff --git a/VtoyTool/vtoytool/00/vtoytool_64 b/VtoyTool/vtoytool/00/vtoytool_64
new file mode 100644 (file)
index 0000000..8b89d0d
Binary files /dev/null and b/VtoyTool/vtoytool/00/vtoytool_64 differ
diff --git a/VtoyTool/vtoytool/01/vtoytool_64 b/VtoyTool/vtoytool/01/vtoytool_64
new file mode 100644 (file)
index 0000000..7628e2c
Binary files /dev/null and b/VtoyTool/vtoytool/01/vtoytool_64 differ
diff --git a/vtoyfat/build.sh b/vtoyfat/build.sh
new file mode 100644 (file)
index 0000000..3aa70df
--- /dev/null
@@ -0,0 +1,15 @@
+#!/bin/sh
+
+rm -f vtoyfat_64
+rm -f vtoyfat_32
+
+gcc -O2 -D_FILE_OFFSET_BITS=64 vtoyfat_linux.c -Ifat_io_lib/include fat_io_lib/lib/libfat_io_64.a -o vtoyfat_64
+gcc -m32 -O2 -D_FILE_OFFSET_BITS=64 vtoyfat_linux.c -Ifat_io_lib/include fat_io_lib/lib/libfat_io_32.a -o vtoyfat_32
+
+if [ -e vtoyfat_64 ] && [ -e vtoyfat_32 ]; then
+    echo -e "\n===== success $name =======\n"
+    [ -d ../INSTALL/tool/ ] && mv vtoyfat_32 ../INSTALL/tool/ && mv vtoyfat_64 ../INSTALL/tool/
+else
+    echo -e "\n===== failed =======\n"
+    exit 1
+fi
diff --git a/vtoyfat/fat_io_lib/README.txt b/vtoyfat/fat_io_lib/README.txt
new file mode 100644 (file)
index 0000000..5f5111b
--- /dev/null
@@ -0,0 +1,3 @@
+========= About fat_io_lib =========
+1. Download fat_io_lib source code from http://ultra-embedded.com/releases/fat_io_lib.zip
+2. decompress the code and run buildlib.sh
diff --git a/vtoyfat/fat_io_lib/buildlib.sh b/vtoyfat/fat_io_lib/buildlib.sh
new file mode 100644 (file)
index 0000000..11ca235
--- /dev/null
@@ -0,0 +1,25 @@
+#!/bin/sh
+
+rm -rf include
+rm -rf lib
+
+cd release
+gcc -O2 -D_FILE_OFFSET_BITS=64 fat*.c -c
+ar -rc libfat_io_64.a *.o
+rm -f *.o
+
+
+gcc -m32 -O2 -D_FILE_OFFSET_BITS=64 fat*.c -c
+ar -rc libfat_io_32.a *.o
+rm -f *.o
+
+
+cd -
+
+mkdir lib
+mkdir include
+
+mv release/*.a lib/
+cp -a release/*.h include/
+
+
diff --git a/vtoyfat/vtoyfat_linux.c b/vtoyfat/vtoyfat_linux.c
new file mode 100644 (file)
index 0000000..cf93ebd
--- /dev/null
@@ -0,0 +1,127 @@
+/******************************************************************************
+ * vtoyfat.c  ---- Parse fat file system
+ *
+ * Copyright (c) 2020, longpanda <admin@ventoy.net>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/stat.h> 
+#include <fcntl.h>
+
+#include <fat_filelib.h>
+
+static int g_disk_fd = 0;
+
+static int vtoy_disk_read(uint32 sector, uint8 *buffer, uint32 sector_count)
+{
+    lseek(g_disk_fd, sector * 512, SEEK_SET);
+    read(g_disk_fd, buffer, sector_count * 512);
+    
+    return 1;
+}
+
+static int get_ventoy_version(void)
+{
+    int rc = 1;
+    int size = 0;
+    char *buf = NULL;
+    char *pos = NULL;
+    char *end = NULL;
+    void *flfile = NULL;
+
+    flfile = fl_fopen("/grub/grub.cfg", "rb");
+    if (flfile)
+    {
+        fl_fseek(flfile, 0, SEEK_END);
+        size = (int)fl_ftell(flfile);
+
+        fl_fseek(flfile, 0, SEEK_SET);
+
+        buf = malloc(size + 1);
+        if (buf)
+        {
+            fl_fread(buf, 1, size, flfile);
+            buf[size] = 0;
+
+            pos = strstr(buf, "VENTOY_VERSION=");
+            if (pos)
+            {
+                pos += strlen("VENTOY_VERSION=");
+                if (*pos == '"')
+                {
+                    pos++;
+                }
+
+                end = pos;
+                while (*end != 0 && *end != '"' && *end != '\r' && *end != '\n')
+                {
+                    end++;
+                }
+
+                *end = 0;
+
+                printf("%s\n", pos);
+                rc = 0;
+            }
+            free(buf);
+        }
+
+        fl_fclose(flfile);
+    }
+
+    return rc;
+}
+
+int main(int argc, char **argv)
+{
+    int rc = 1;
+
+    if (argc != 2)
+    {   
+        printf("Usage: vtoyfat /dev/sdb \n");
+        return 1;
+    }
+    
+    if (argv[1][0] == '-' && argv[1][1] == 'T')
+    {
+        return 0;
+    }
+    
+    g_disk_fd = open(argv[1], O_RDONLY);
+    if (g_disk_fd < 0)
+    {
+        printf("Failed to open %s\n", argv[1]);
+        return 1;
+    }
+
+    fl_init();
+
+    if (0 == fl_attach_media(vtoy_disk_read, NULL))
+    {
+        rc = get_ventoy_version();
+    }
+
+    fl_shutdown();
+
+    close(g_disk_fd);
+
+    return rc;
+}
+
diff --git a/vtoyjump/vtoyjump.sln b/vtoyjump/vtoyjump.sln
new file mode 100644 (file)
index 0000000..d8154d9
--- /dev/null
@@ -0,0 +1,28 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2013
+VisualStudioVersion = 12.0.21005.1
+MinimumVisualStudioVersion = 10.0.40219.1
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "vtoyjump", "vtoyjump\vtoyjump.vcxproj", "{0C6C9787-1CFD-4F31-B5FF-6FCDBB6F2321}"
+EndProject
+Global
+       GlobalSection(SolutionConfigurationPlatforms) = preSolution
+               Debug|Win32 = Debug|Win32
+               Debug|x64 = Debug|x64
+               Release|Win32 = Release|Win32
+               Release|x64 = Release|x64
+       EndGlobalSection
+       GlobalSection(ProjectConfigurationPlatforms) = postSolution
+               {0C6C9787-1CFD-4F31-B5FF-6FCDBB6F2321}.Debug|Win32.ActiveCfg = Debug|Win32
+               {0C6C9787-1CFD-4F31-B5FF-6FCDBB6F2321}.Debug|Win32.Build.0 = Debug|Win32
+               {0C6C9787-1CFD-4F31-B5FF-6FCDBB6F2321}.Debug|x64.ActiveCfg = Debug|x64
+               {0C6C9787-1CFD-4F31-B5FF-6FCDBB6F2321}.Debug|x64.Build.0 = Debug|x64
+               {0C6C9787-1CFD-4F31-B5FF-6FCDBB6F2321}.Release|Win32.ActiveCfg = Release|Win32
+               {0C6C9787-1CFD-4F31-B5FF-6FCDBB6F2321}.Release|Win32.Build.0 = Release|Win32
+               {0C6C9787-1CFD-4F31-B5FF-6FCDBB6F2321}.Release|x64.ActiveCfg = Release|x64
+               {0C6C9787-1CFD-4F31-B5FF-6FCDBB6F2321}.Release|x64.Build.0 = Release|x64
+       EndGlobalSection
+       GlobalSection(SolutionProperties) = preSolution
+               HideSolutionNode = FALSE
+       EndGlobalSection
+EndGlobal
diff --git a/vtoyjump/vtoyjump/fat_io_lib/API.txt b/vtoyjump/vtoyjump/fat_io_lib/API.txt
new file mode 100644 (file)
index 0000000..666cbe5
--- /dev/null
@@ -0,0 +1,22 @@
+File IO Lib API
+-=-=-=-=-=-=-=-=-
+
+void fl_init(void)
+
+  Called to initialize FAT IO library.
+  This should be called prior to any other functions.
+
+void fl_attach_locks(void (*lock)(void), void (*unlock)(void))
+
+  [Optional] File system thread safety locking functions.
+  For thread safe operation, you should provide lock() and unlock() functions.
+  Note that locking primitive used must support recursive locking, i.e lock() called within an already \91locked\92 region.
+
+int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)
+
+  This function is used to attach system specific disk/media access functions.  
+  This should be done subsequent to calling fl_init() and fl_attach_locks() (if locking required).
+
+void fl_shutdown(void)
+
+  Shutdown the FAT IO library. This purges any un-saved data back to disk.
diff --git a/vtoyjump/vtoyjump/fat_io_lib/COPYRIGHT.txt b/vtoyjump/vtoyjump/fat_io_lib/COPYRIGHT.txt
new file mode 100644 (file)
index 0000000..0d728f3
--- /dev/null
@@ -0,0 +1,345 @@
+                   GNU GENERAL PUBLIC LICENSE
+                      Version 2, June 1991
+
+ Copyright (C) 1989, 1991 Free Software Foundation, Inc.
+                       59 Temple Place, Suite 330, Boston, MA  02111-1307  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.
+
+    <one line to give the program's name and a brief idea of what it does.>
+    Copyright (C) <year>  <name of author>
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 2 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program; if not, write to the Free Software
+    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  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.
+
+  <signature of Ty Coon>, 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.
diff --git a/vtoyjump/vtoyjump/fat_io_lib/Configuration.txt b/vtoyjump/vtoyjump/fat_io_lib/Configuration.txt
new file mode 100644 (file)
index 0000000..370f1da
--- /dev/null
@@ -0,0 +1,53 @@
+File IO Lib Options
+-=-=-=-=-=-=-=-=-=-
+
+See defines in fat_opts.h:
+
+FATFS_IS_LITTLE_ENDIAN         [1/0]
+  Which endian is your system? Set to 1 for little endian, 0 for big endian.
+
+FATFS_MAX_LONG_FILENAME        [260]
+  By default, 260 characters (max LFN length). Increase this to support greater path depths.
+
+FATFS_MAX_OPEN_FILES   
+  The more files you wish to have concurrently open, the greater this number should be.
+  This increases the number of FL_FILE file structures in the library, each of these is around 1K in size (assuming 512 byte sectors).
+
+FAT_BUFFER_SECTORS
+  Minimum is 1, more increases performance.
+  This defines how many FAT sectors can be buffered per FAT_BUFFER entry.
+
+FAT_BUFFERS
+  Minimum is 1, more increases performance.
+  This defines how many FAT buffer entries are available.
+  Memory usage is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE
+
+FATFS_INC_WRITE_SUPPORT
+  Support file write functionality.
+
+FAT_SECTOR_SIZE
+  Sector size used by buffers. Most likely to be 512 bytes (standard for ATA/IDE).
+
+FAT_PRINTF
+  A define that allows the File IO library to print to console/stdout. 
+  Provide your own printf function if printf not available.
+
+FAT_CLUSTER_CACHE_ENTRIES
+  Size of cluster chain cache (can be undefined if not required).
+  Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2
+  Improves access speed considerably.
+
+FATFS_INC_LFN_SUPPORT  [1/0]
+  Enable/Disable support for long filenames.
+
+FATFS_DIR_LIST_SUPPORT         [1/0]
+  Include support for directory listing.
+
+FATFS_INC_TIME_DATE_SUPPORT  [1/0]
+  Use time/date functions provided by time.h to update creation & modification timestamps.
+
+FATFS_INC_FORMAT_SUPPORT
+  Include support for formatting disks (FAT16 only).
+
+FAT_PRINTF_NOINC_STDIO
+  Disable use of printf & inclusion of stdio.h
diff --git a/vtoyjump/vtoyjump/fat_io_lib/History.txt b/vtoyjump/vtoyjump/fat_io_lib/History.txt
new file mode 100644 (file)
index 0000000..b0bdc90
--- /dev/null
@@ -0,0 +1,24 @@
+Revision History
+-=-=-=-=-=-=-=-=-
+v2.6.11 - Fix compilation with GCC on 64-bit machines
+v2.6.10 - Added support for FAT32 format.
+V2.6.9  - Added support for time & date handling.
+V2.6.8  - Fixed error with FSINFO sector write.
+V2.6.7  - Added fgets().
+          Fixed C warnings, removed dependancy on some string.h functions.
+V2.6.6  \96 Massive read + write performance  improvements.
+V2.6.5  \96 Bug fixes for big endian systems.
+V2.6.4  \96 Further bug fixes and performance improvements for write operations.
+V2.6.3  \96 Peformance improvements, FAT16 formatting support. Various bug fixes.
+V2.6    - Basic support for FAT16 added (18-04-10).
+V2.5    - Code cleaned up. Many bugs fixed. Thread safety functions added.
+V2.x    - Write support added as well as better stdio like API.
+V1.0    - Rewrite of all code to enable multiple files to be opened and provides a 
+          better file API.
+         Also better string matching, and generally better C code than origonal 
+          version.
+V0.1c   - Fetch_ID_Max_LBA() function added to retrieve Drive infomation and stoping 
+          the drive reads from addressing a sector that is out of range.
+V0.1b   - fopen(), fgetc(), fopenDIR() using new software stack for IDE and FAT32 
+          access.
+V0.1a   - First release (27/12/03); fopen(), fgetc() unbuffered reads.
diff --git a/vtoyjump/vtoyjump/fat_io_lib/License.txt b/vtoyjump/vtoyjump/fat_io_lib/License.txt
new file mode 100644 (file)
index 0000000..3e78364
--- /dev/null
@@ -0,0 +1,10 @@
+FAT File IO Library License
+-=-=-=-=-=-=-=-=-=-=-=-=-=-
+
+This versions license: GPL
+
+If you include GPL software in your project, you must release the source code of that project too.
+
+If you would like a version with a more permissive license for use in closed source commercial applications please contact me for details.
+
+Email: admin@ultra-embedded.com
diff --git a/vtoyjump/vtoyjump/fat_io_lib/Media b/vtoyjump/vtoyjump/fat_io_lib/Media
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/vtoyjump/vtoyjump/fat_io_lib/Media Access API.txt b/vtoyjump/vtoyjump/fat_io_lib/Media Access API.txt
new file mode 100644 (file)
index 0000000..14ceed3
--- /dev/null
@@ -0,0 +1,40 @@
+Media Access API
+-=-=-=-=-=-=-=-=-
+
+int media_read(uint32 sector, uint8 *buffer, uint32 sector_count)
+
+Params:
+       Sector: 32-bit sector number
+       Buffer: Target buffer to read n sectors of data into.
+       Sector_count: Number of sectors to read.
+
+Return: 
+       int, 1 = success, 0 = failure.
+
+Description:
+   Application/target specific disk/media read function.
+   Sector number (sectors are usually 512 byte pages) to read.
+
+Media Write API
+
+int media_write(uint32 sector, uint8 *buffer, uint32 sector_count)
+
+Params:
+       Sector: 32-bit sector number
+       Buffer: Target buffer to write n sectors of data from.
+       Sector_count: Number of sectors to write.
+
+Return: 
+       int, 1 = success, 0 = failure.
+
+Description:
+   Application/target specific disk/media write function.
+   Sector number (sectors are usually 512 byte pages) to write to.
+
+File IO Library Linkage
+   Use the following API to attach the media IO functions to the File IO library.
+
+   int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)
+
+
+
diff --git a/vtoyjump/vtoyjump/fat_io_lib/example.c.txt b/vtoyjump/vtoyjump/fat_io_lib/example.c.txt
new file mode 100644 (file)
index 0000000..340f0bd
--- /dev/null
@@ -0,0 +1,87 @@
+#include <stdio.h>
+#include "fat_filelib.h"
+
+int media_init()
+{
+    // ...
+    return 1;
+}
+
+int media_read(unsigned long sector, unsigned char *buffer, unsigned long sector_count)
+{
+    unsigned long i;
+
+    for (i=0;i<sector_count;i++)
+    {
+        // ...
+        // Add platform specific sector (512 bytes) read code here
+        //..
+
+        sector ++;
+        buffer += 512;
+    }
+
+    return 1;
+}
+
+int media_write(unsigned long sector, unsigned char *buffer, unsigned long sector_count)
+{
+    unsigned long i;
+
+    for (i=0;i<sector_count;i++)
+    {
+        // ...
+        // Add platform specific sector (512 bytes) write code here
+        //..
+
+        sector ++;
+        buffer += 512;
+    }
+
+    return 1;
+}
+
+void main()
+{
+    FL_FILE *file;
+
+    // Initialise media
+    media_init();
+
+    // Initialise File IO Library
+    fl_init();
+
+    // Attach media access functions to library
+    if (fl_attach_media(media_read, media_write) != FAT_INIT_OK)
+    {
+        printf("ERROR: Media attach failed\n");
+        return; 
+    }
+
+    // List root directory
+    fl_listdirectory("/");
+
+    // Create File
+    file = fl_fopen("/file.bin", "w");
+    if (file)
+    {
+        // Write some data
+        unsigned char data[] = { 1, 2, 3, 4 };
+        if (fl_fwrite(data, 1, sizeof(data), file) != sizeof(data))
+            printf("ERROR: Write file failed\n");
+    }
+    else
+        printf("ERROR: Create file failed\n");
+
+    // Close file
+    fl_fclose(file);
+
+    // Delete File
+    if (fl_remove("/file.bin") < 0)
+        printf("ERROR: Delete file failed\n");
+
+    // List root directory
+    fl_listdirectory("/");
+
+    fl_shutdown();
+}
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_access.c b/vtoyjump/vtoyjump/fat_io_lib/fat_access.c
new file mode 100644 (file)
index 0000000..0f0cdd5
--- /dev/null
@@ -0,0 +1,904 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//                            FAT16/32 File IO Library
+//                                    V2.6
+//                              Ultra-Embedded.com
+//                            Copyright 2003 - 2012
+//
+//                         Email: admin@ultra-embedded.com
+//
+//                                License: GPL
+//   If you would like a version with a more permissive license for use in
+//   closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_init: Load FAT Parameters
+//-----------------------------------------------------------------------------
+int fatfs_init(struct fatfs *fs)
+{
+    uint8 num_of_fats;
+    uint16 reserved_sectors;
+    uint32 FATSz;
+    uint32 root_dir_sectors;
+    uint32 total_sectors;
+    uint32 data_sectors;
+    uint32 count_of_clusters;
+    uint8 valid_partition = 0;
+
+    fs->currentsector.address = FAT32_INVALID_CLUSTER;
+    fs->currentsector.dirty = 0;
+
+    fs->next_free_cluster = 0; // Invalid
+
+    fatfs_fat_init(fs);
+
+    // Make sure we have a read function (write function is optional)
+    if (!fs->disk_io.read_media)
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // MBR: Sector 0 on the disk
+    // NOTE: Some removeable media does not have this.
+
+    // Load MBR (LBA 0) into the 512 byte buffer
+    if (!fs->disk_io.read_media(0, fs->currentsector.sector, 1))
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // Make Sure 0x55 and 0xAA are at end of sector
+    // (this should be the case regardless of the MBR or boot sector)
+    if (fs->currentsector.sector[SIGNATURE_POSITION] != 0x55 || fs->currentsector.sector[SIGNATURE_POSITION+1] != 0xAA)
+        return FAT_INIT_INVALID_SIGNATURE;
+
+    // Now check again using the access function to prove endian conversion function
+    if (GET_16BIT_WORD(fs->currentsector.sector, SIGNATURE_POSITION) != SIGNATURE_VALUE)
+        return FAT_INIT_ENDIAN_ERROR;
+
+    // Verify packed structures
+    if (sizeof(struct fat_dir_entry) != FAT_DIR_ENTRY_SIZE)
+        return FAT_INIT_STRUCT_PACKING;
+
+    // Check the partition type code
+    switch(fs->currentsector.sector[PARTITION1_TYPECODE_LOCATION])
+    {
+        case 0x0B:
+        case 0x06:
+        case 0x0C:
+        case 0x0E:
+        case 0x0F:
+        case 0x05:
+            valid_partition = 1;
+        break;
+        case 0x00:
+            valid_partition = 0;
+            break;
+        default:
+            if (fs->currentsector.sector[PARTITION1_TYPECODE_LOCATION] <= 0x06)
+                valid_partition = 1;
+        break;
+    }
+
+    // Read LBA Begin for the file system
+    if (valid_partition)
+        fs->lba_begin = GET_32BIT_WORD(fs->currentsector.sector, PARTITION1_LBA_BEGIN_LOCATION);
+    // Else possibly MBR less disk
+    else
+        fs->lba_begin = 0;
+
+    // Load Volume 1 table into sector buffer
+    // (We may already have this in the buffer if MBR less drive!)
+    if (!fs->disk_io.read_media(fs->lba_begin, fs->currentsector.sector, 1))
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // Make sure there are 512 bytes per cluster
+    if (GET_16BIT_WORD(fs->currentsector.sector, 0x0B) != FAT_SECTOR_SIZE)
+        return FAT_INIT_INVALID_SECTOR_SIZE;
+
+    // Load Parameters of FAT partition
+    fs->sectors_per_cluster = fs->currentsector.sector[BPB_SECPERCLUS];
+    reserved_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_RSVDSECCNT);
+    num_of_fats = fs->currentsector.sector[BPB_NUMFATS];
+    fs->root_entry_count = GET_16BIT_WORD(fs->currentsector.sector, BPB_ROOTENTCNT);
+
+    if(GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16) != 0)
+        fs->fat_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16);
+    else
+        fs->fat_sectors = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_FATSZ32);
+
+    // For FAT32 (which this may be)
+    fs->rootdir_first_cluster = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_ROOTCLUS);
+    fs->fs_info_sector = GET_16BIT_WORD(fs->currentsector.sector, BPB_FAT32_FSINFO);
+
+    // For FAT16 (which this may be), rootdir_first_cluster is actuall rootdir_first_sector
+    fs->rootdir_first_sector = reserved_sectors + (num_of_fats * fs->fat_sectors);
+    fs->rootdir_sectors = ((fs->root_entry_count * 32) + (FAT_SECTOR_SIZE - 1)) / FAT_SECTOR_SIZE;
+
+    // First FAT LBA address
+    fs->fat_begin_lba = fs->lba_begin + reserved_sectors;
+
+    // The address of the first data cluster on this volume
+    fs->cluster_begin_lba = fs->fat_begin_lba + (num_of_fats * fs->fat_sectors);
+
+    if (GET_16BIT_WORD(fs->currentsector.sector, 0x1FE) != 0xAA55) // This signature should be AA55
+        return FAT_INIT_INVALID_SIGNATURE;
+
+    // Calculate the root dir sectors
+    root_dir_sectors = ((GET_16BIT_WORD(fs->currentsector.sector, BPB_ROOTENTCNT) * 32) + (GET_16BIT_WORD(fs->currentsector.sector, BPB_BYTSPERSEC) - 1)) / GET_16BIT_WORD(fs->currentsector.sector, BPB_BYTSPERSEC);
+
+    if(GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16) != 0)
+        FATSz = GET_16BIT_WORD(fs->currentsector.sector, BPB_FATSZ16);
+    else
+        FATSz = GET_32BIT_WORD(fs->currentsector.sector, BPB_FAT32_FATSZ32);
+
+    if(GET_16BIT_WORD(fs->currentsector.sector, BPB_TOTSEC16) != 0)
+        total_sectors = GET_16BIT_WORD(fs->currentsector.sector, BPB_TOTSEC16);
+    else
+        total_sectors = GET_32BIT_WORD(fs->currentsector.sector, BPB_TOTSEC32);
+
+    data_sectors = total_sectors - (GET_16BIT_WORD(fs->currentsector.sector, BPB_RSVDSECCNT) + (fs->currentsector.sector[BPB_NUMFATS] * FATSz) + root_dir_sectors);
+
+    // Find out which version of FAT this is...
+    if (fs->sectors_per_cluster != 0)
+    {
+        count_of_clusters = data_sectors / fs->sectors_per_cluster;
+
+        if(count_of_clusters < 4085)
+            // Volume is FAT12
+            return FAT_INIT_WRONG_FILESYS_TYPE;
+        else if(count_of_clusters < 65525)
+        {
+            // Clear this FAT32 specific param
+            fs->rootdir_first_cluster = 0;
+
+            // Volume is FAT16
+            fs->fat_type = FAT_TYPE_16;
+            return FAT_INIT_OK;
+        }
+        else
+        {
+            // Volume is FAT32
+            fs->fat_type = FAT_TYPE_32;
+            return FAT_INIT_OK;
+        }
+    }
+    else
+        return FAT_INIT_WRONG_FILESYS_TYPE;
+}
+//-----------------------------------------------------------------------------
+// fatfs_lba_of_cluster: This function converts a cluster number into a sector /
+// LBA number.
+//-----------------------------------------------------------------------------
+uint32 fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number)
+{
+    if (fs->fat_type == FAT_TYPE_16)
+        return (fs->cluster_begin_lba + (fs->root_entry_count * 32 / FAT_SECTOR_SIZE) + ((Cluster_Number-2) * fs->sectors_per_cluster));
+    else
+        return ((fs->cluster_begin_lba + ((Cluster_Number-2)*fs->sectors_per_cluster)));
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_read:
+//-----------------------------------------------------------------------------
+int fatfs_sector_read(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count)
+{
+    return fs->disk_io.read_media(lba, target, count);
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_write:
+//-----------------------------------------------------------------------------
+int fatfs_sector_write(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count)
+{
+    return fs->disk_io.write_media(lba, target, count);
+}
+//-----------------------------------------------------------------------------
+// fatfs_sector_reader: From the provided startcluster and sector offset
+// Returns True if success, returns False if not (including if read out of range)
+//-----------------------------------------------------------------------------
+int fatfs_sector_reader(struct fatfs *fs, uint32 start_cluster, uint32 offset, uint8 *target)
+{
+    uint32 sector_to_read = 0;
+    uint32 cluster_to_read = 0;
+    uint32 cluster_chain = 0;
+    uint32 i;
+    uint32 lba;
+
+    // FAT16 Root directory
+    if (fs->fat_type == FAT_TYPE_16 && start_cluster == 0)
+    {
+        if (offset < fs->rootdir_sectors)
+            lba = fs->lba_begin + fs->rootdir_first_sector + offset;
+        else
+            return 0;
+    }
+    // FAT16/32 Other
+    else
+    {
+        // Set start of cluster chain to initial value
+        cluster_chain = start_cluster;
+
+        // Find parameters
+        cluster_to_read = offset / fs->sectors_per_cluster;
+        sector_to_read = offset - (cluster_to_read*fs->sectors_per_cluster);
+
+        // Follow chain to find cluster to read
+        for (i=0; i<cluster_to_read; i++)
+            cluster_chain = fatfs_find_next_cluster(fs, cluster_chain);
+
+        // If end of cluster chain then return false
+        if (cluster_chain == FAT32_LAST_CLUSTER)
+            return 0;
+
+        // Calculate sector address
+        lba = fatfs_lba_of_cluster(fs, cluster_chain)+sector_to_read;
+    }
+
+    // User provided target array
+    if (target)
+        return fs->disk_io.read_media(lba, target, 1);
+    // Else read sector if not already loaded
+    else if (lba != fs->currentsector.address)
+    {
+        fs->currentsector.address = lba;
+        return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
+    }
+    else
+        return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_read_sector: Read from the provided cluster and sector offset
+// Returns True if success, returns False if not
+//-----------------------------------------------------------------------------
+int fatfs_read_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target)
+{
+    // FAT16 Root directory
+    if (fs->fat_type == FAT_TYPE_16 && cluster == 0)
+    {
+        uint32 lba;
+
+        // In FAT16, there are a limited amount of sectors in root dir!
+        if (sector < fs->rootdir_sectors)
+            lba = fs->lba_begin + fs->rootdir_first_sector + sector;
+        else
+            return 0;
+
+        // User target buffer passed in
+        if (target)
+        {
+            // Read from disk
+            return fs->disk_io.read_media(lba, target, 1);
+        }
+        else
+        {
+            // Calculate read address
+            fs->currentsector.address = lba;
+
+            // Read from disk
+            return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
+        }
+    }
+    // FAT16/32 Other
+    else
+    {
+        // User target buffer passed in
+        if (target)
+        {
+            // Calculate read address
+            uint32 lba = fatfs_lba_of_cluster(fs, cluster) + sector;
+
+            // Read from disk
+            return fs->disk_io.read_media(lba, target, 1);
+        }
+        else
+        {
+            // Calculate write address
+            fs->currentsector.address = fatfs_lba_of_cluster(fs, cluster)+sector;
+
+            // Read from disk
+            return fs->disk_io.read_media(fs->currentsector.address, fs->currentsector.sector, 1);
+        }
+    }
+}
+//-----------------------------------------------------------------------------
+// fatfs_write_sector: Write to the provided cluster and sector offset
+// Returns True if success, returns False if not
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_write_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target)
+{
+    // No write access?
+    if (!fs->disk_io.write_media)
+        return 0;
+
+    // FAT16 Root directory
+    if (fs->fat_type == FAT_TYPE_16 && cluster == 0)
+    {
+        uint32 lba;
+
+        // In FAT16 we cannot extend the root dir!
+        if (sector < fs->rootdir_sectors)
+            lba = fs->lba_begin + fs->rootdir_first_sector + sector;
+        else
+            return 0;
+
+        // User target buffer passed in
+        if (target)
+        {
+            // Write to disk
+            return fs->disk_io.write_media(lba, target, 1);
+        }
+        else
+        {
+            // Calculate write address
+            fs->currentsector.address = lba;
+
+            // Write to disk
+            return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+        }
+    }
+    // FAT16/32 Other
+    else
+    {
+        // User target buffer passed in
+        if (target)
+        {
+            // Calculate write address
+            uint32 lba = fatfs_lba_of_cluster(fs, cluster) + sector;
+
+            // Write to disk
+            return fs->disk_io.write_media(lba, target, 1);
+        }
+        else
+        {
+            // Calculate write address
+            fs->currentsector.address = fatfs_lba_of_cluster(fs, cluster)+sector;
+
+            // Write to disk
+            return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+        }
+    }
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_show_details: Show the details about the filesystem
+//-----------------------------------------------------------------------------
+void fatfs_show_details(struct fatfs *fs)
+{
+    FAT_PRINTF(("FAT details:\r\n"));
+    FAT_PRINTF((" Type =%s", (fs->fat_type == FAT_TYPE_32) ? "FAT32": "FAT16"));
+    FAT_PRINTF((" Root Dir First Cluster = %x\r\n", fs->rootdir_first_cluster));
+    FAT_PRINTF((" FAT Begin LBA = 0x%x\r\n",fs->fat_begin_lba));
+    FAT_PRINTF((" Cluster Begin LBA = 0x%x\r\n",fs->cluster_begin_lba));
+    FAT_PRINTF((" Sectors Per Cluster = %d\r\n", fs->sectors_per_cluster));
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_root_cluster: Get the root dir cluster
+//-----------------------------------------------------------------------------
+uint32 fatfs_get_root_cluster(struct fatfs *fs)
+{
+    // NOTE: On FAT16 this will be 0 which has a special meaning...
+    return fs->rootdir_first_cluster;
+}
+//-------------------------------------------------------------
+// fatfs_get_file_entry: Find the file entry for a filename
+//-------------------------------------------------------------
+uint32 fatfs_get_file_entry(struct fatfs *fs, uint32 Cluster, char *name_to_find, struct fat_dir_entry *sfEntry)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    uint8 i=0;
+    int x=0;
+    char *long_filename = NULL;
+    char short_filename[13];
+    struct lfn_cache lfn;
+    int dotRequired = 0;
+    struct fat_dir_entry *directoryEntry;
+
+    fatfs_lfn_cache_init(&lfn, 1);
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if (fatfs_entry_lfn_text(directoryEntry) )
+                    fatfs_lfn_cache_entry(&lfn, fs->currentsector.sector+recordoffset);
+
+                // If Invalid record found delete any long file name information collated
+                else if (fatfs_entry_lfn_invalid(directoryEntry) )
+                    fatfs_lfn_cache_init(&lfn, 0);
+
+                // Normal SFN Entry and Long text exists
+                else if (fatfs_entry_lfn_exists(&lfn, directoryEntry) )
+                {
+                    long_filename = fatfs_lfn_cache_get(&lfn);
+
+                    // Compare names to see if they match
+                    if (fatfs_compare_names(long_filename, name_to_find))
+                    {
+                        memcpy(sfEntry,directoryEntry,sizeof(struct fat_dir_entry));
+                        return 1;
+                    }
+
+                    fatfs_lfn_cache_init(&lfn, 0);
+                }
+                else
+#endif
+                // Normal Entry, only 8.3 Text
+                if (fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    memset(short_filename, 0, sizeof(short_filename));
+
+                    // Copy name to string
+                    for (i=0; i<8; i++)
+                        short_filename[i] = directoryEntry->Name[i];
+
+                    // Extension
+                    dotRequired = 0;
+                    for (i=8; i<11; i++)
+                    {
+                        short_filename[i+1] = directoryEntry->Name[i];
+                        if (directoryEntry->Name[i] != ' ')
+                            dotRequired = 1;
+                    }
+
+                    // Dot only required if extension present
+                    if (dotRequired)
+                    {
+                        // If not . or .. entry
+                        if (short_filename[0]!='.')
+                            short_filename[8] = '.';
+                        else
+                            short_filename[8] = ' ';
+                    }
+                    else
+                        short_filename[8] = ' ';
+
+                    // Compare names to see if they match
+                    if (fatfs_compare_names(short_filename, name_to_find))
+                    {
+                        memcpy(sfEntry,directoryEntry,sizeof(struct fat_dir_entry));
+                        return 1;
+                    }
+
+                    fatfs_lfn_cache_init(&lfn, 0);
+                }
+            } // End of if
+        }
+        else
+            break;
+    } // End of while loop
+
+    return 0;
+}
+//-------------------------------------------------------------
+// fatfs_sfn_exists: Check if a short filename exists.
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_sfn_exists(struct fatfs *fs, uint32 Cluster, char *shortname)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    int x=0;
+    struct fat_dir_entry *directoryEntry;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if (fatfs_entry_lfn_text(directoryEntry) )
+                    ;
+
+                // If Invalid record found delete any long file name information collated
+                else if (fatfs_entry_lfn_invalid(directoryEntry) )
+                    ;
+                else
+#endif
+                // Normal Entry, only 8.3 Text
+                if (fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    if (strncmp((const char*)directoryEntry->Name, shortname, 11)==0)
+                        return 1;
+                }
+            } // End of if
+        }
+        else
+            break;
+    } // End of while loop
+
+    return 0;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_update_timestamps: Update date/time details
+//-------------------------------------------------------------
+#if FATFS_INC_TIME_DATE_SUPPORT
+int fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access)
+{
+    time_t time_now;
+    struct tm * time_info;
+    uint16 fat_time;
+    uint16 fat_date;
+
+    // Get system time
+    time(&time_now);
+
+    // Convert to local time
+    time_info = localtime(&time_now);
+
+    // Convert time to FAT format
+    fat_time = fatfs_convert_to_fat_time(time_info->tm_hour, time_info->tm_min, time_info->tm_sec);
+
+    // Convert date to FAT format
+    fat_date = fatfs_convert_to_fat_date(time_info->tm_mday, time_info->tm_mon + 1, time_info->tm_year + 1900);
+
+    // Update requested fields
+    if (create)
+    {
+        directoryEntry->CrtTime[1] = fat_time >> 8;
+        directoryEntry->CrtTime[0] = fat_time >> 0;
+        directoryEntry->CrtDate[1] = fat_date >> 8;
+        directoryEntry->CrtDate[0] = fat_date >> 0;
+    }
+
+    if (modify)
+    {
+        directoryEntry->WrtTime[1] = fat_time >> 8;
+        directoryEntry->WrtTime[0] = fat_time >> 0;
+        directoryEntry->WrtDate[1] = fat_date >> 8;
+        directoryEntry->WrtDate[0] = fat_date >> 0;
+    }
+
+    if (access)
+    {
+        directoryEntry->LstAccDate[1] = fat_time >> 8;
+        directoryEntry->LstAccDate[0] = fat_time >> 0;
+        directoryEntry->LstAccDate[1] = fat_date >> 8;
+        directoryEntry->LstAccDate[0] = fat_date >> 0;
+    }
+
+    return 1;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_update_file_length: Find a SFN entry and update it
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_update_file_length(struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    int x=0;
+    struct fat_dir_entry *directoryEntry;
+
+    // No write access?
+    if (!fs->disk_io.write_media)
+        return 0;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if (fatfs_entry_lfn_text(directoryEntry) )
+                    ;
+
+                // If Invalid record found delete any long file name information collated
+                else if (fatfs_entry_lfn_invalid(directoryEntry) )
+                    ;
+
+                // Normal Entry, only 8.3 Text
+                else
+#endif
+                if (fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    if (strncmp((const char*)directoryEntry->Name, shortname, 11)==0)
+                    {
+                        directoryEntry->FileSize = FAT_HTONL(fileLength);
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                        // Update access / modify time & date
+                        fatfs_update_timestamps(directoryEntry, 0, 1, 1);
+#endif
+
+                        // Update sfn entry
+                        memcpy((uint8*)(fs->currentsector.sector+recordoffset), (uint8*)directoryEntry, sizeof(struct fat_dir_entry));
+
+                        // Write sector back
+                        return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+                    }
+                }
+            } // End of if
+        }
+        else
+            break;
+    } // End of while loop
+
+    return 0;
+}
+#endif
+//-------------------------------------------------------------
+// fatfs_mark_file_deleted: Find a SFN entry and mark if as deleted
+// NOTE: shortname is XXXXXXXXYYY not XXXXXXXX.YYY
+//-------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_mark_file_deleted(struct fatfs *fs, uint32 Cluster, char *shortname)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    int x=0;
+    struct fat_dir_entry *directoryEntry;
+
+    // No write access?
+    if (!fs->disk_io.write_media)
+        return 0;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, Cluster, x++, 0)) // If sector read was successfull
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if (fatfs_entry_lfn_text(directoryEntry) )
+                    ;
+
+                // If Invalid record found delete any long file name information collated
+                else if (fatfs_entry_lfn_invalid(directoryEntry) )
+                    ;
+
+                // Normal Entry, only 8.3 Text
+                else
+#endif
+                if (fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    if (strncmp((const char *)directoryEntry->Name, shortname, 11)==0)
+                    {
+                        // Mark as deleted
+                        directoryEntry->Name[0] = FILE_HEADER_DELETED;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                        // Update access / modify time & date
+                        fatfs_update_timestamps(directoryEntry, 0, 1, 1);
+#endif
+
+                        // Update sfn entry
+                        memcpy((uint8*)(fs->currentsector.sector+recordoffset), (uint8*)directoryEntry, sizeof(struct fat_dir_entry));
+
+                        // Write sector back
+                        return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+                    }
+                }
+            } // End of if
+        }
+        else
+            break;
+    } // End of while loop
+
+    return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_list_directory_start: Initialise a directory listing procedure
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+void fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster)
+{
+    dirls->cluster = StartCluster;
+    dirls->sector = 0;
+    dirls->offset = 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_list_directory_next: Get the next entry in the directory.
+// Returns: 1 = found, 0 = end of listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fatfs_list_directory_next(struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry)
+{
+    uint8 i,item;
+    uint16 recordoffset;
+    struct fat_dir_entry *directoryEntry;
+    char *long_filename = NULL;
+    char short_filename[13];
+    struct lfn_cache lfn;
+    int dotRequired = 0;
+    int result = 0;
+
+    // Initialise LFN cache first
+    fatfs_lfn_cache_init(&lfn, 0);
+
+    while (1)
+    {
+        // If data read OK
+        if (fatfs_sector_reader(fs, dirls->cluster, dirls->sector, 0))
+        {
+            // Maximum of 16 directory entries
+            for (item = dirls->offset; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Increase directory offset
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+#if FATFS_INC_LFN_SUPPORT
+                // Long File Name Text Found
+                if ( fatfs_entry_lfn_text(directoryEntry) )
+                    fatfs_lfn_cache_entry(&lfn, fs->currentsector.sector+recordoffset);
+
+                // If Invalid record found delete any long file name information collated
+                else if ( fatfs_entry_lfn_invalid(directoryEntry) )
+                    fatfs_lfn_cache_init(&lfn, 0);
+
+                // Normal SFN Entry and Long text exists
+                else if (fatfs_entry_lfn_exists(&lfn, directoryEntry) )
+                {
+                    // Get text
+                    long_filename = fatfs_lfn_cache_get(&lfn);
+                    strcpy_s(entry->filename, FATFS_MAX_LONG_FILENAME - 1, long_filename);
+
+                    if (fatfs_entry_is_dir(directoryEntry))
+                        entry->is_dir = 1;
+                    else
+                        entry->is_dir = 0;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                    // Get time / dates
+                    entry->create_time = ((uint16)directoryEntry->CrtTime[1] << 8) | directoryEntry->CrtTime[0];
+                    entry->create_date = ((uint16)directoryEntry->CrtDate[1] << 8) | directoryEntry->CrtDate[0];
+                    entry->access_date = ((uint16)directoryEntry->LstAccDate[1] << 8) | directoryEntry->LstAccDate[0];
+                    entry->write_time  = ((uint16)directoryEntry->WrtTime[1] << 8) | directoryEntry->WrtTime[0];
+                    entry->write_date  = ((uint16)directoryEntry->WrtDate[1] << 8) | directoryEntry->WrtDate[0];
+#endif
+
+                    entry->size = FAT_HTONL(directoryEntry->FileSize);
+                    entry->cluster = (FAT_HTONS(directoryEntry->FstClusHI)<<16) | FAT_HTONS(directoryEntry->FstClusLO);
+
+                    // Next starting position
+                    dirls->offset = item + 1;
+                    result = 1;
+                    return 1;
+                }
+                // Normal Entry, only 8.3 Text
+                else
+#endif
+                if ( fatfs_entry_sfn_only(directoryEntry) )
+                {
+                    fatfs_lfn_cache_init(&lfn, 0);
+
+                    memset(short_filename, 0, sizeof(short_filename));
+
+                    // Copy name to string
+                    for (i=0; i<8; i++)
+                        short_filename[i] = directoryEntry->Name[i];
+
+                    // Extension
+                    dotRequired = 0;
+                    for (i=8; i<11; i++)
+                    {
+                        short_filename[i+1] = directoryEntry->Name[i];
+                        if (directoryEntry->Name[i] != ' ')
+                            dotRequired = 1;
+                    }
+
+                    // Dot only required if extension present
+                    if (dotRequired)
+                    {
+                        // If not . or .. entry
+                        if (short_filename[0]!='.')
+                            short_filename[8] = '.';
+                        else
+                            short_filename[8] = ' ';
+                    }
+                    else
+                        short_filename[8] = ' ';
+
+                    fatfs_get_sfn_display_name(entry->filename, short_filename);
+
+                    if (fatfs_entry_is_dir(directoryEntry))
+                        entry->is_dir = 1;
+                    else
+                        entry->is_dir = 0;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                    // Get time / dates
+                    entry->create_time = ((uint16)directoryEntry->CrtTime[1] << 8) | directoryEntry->CrtTime[0];
+                    entry->create_date = ((uint16)directoryEntry->CrtDate[1] << 8) | directoryEntry->CrtDate[0];
+                    entry->access_date = ((uint16)directoryEntry->LstAccDate[1] << 8) | directoryEntry->LstAccDate[0];
+                    entry->write_time  = ((uint16)directoryEntry->WrtTime[1] << 8) | directoryEntry->WrtTime[0];
+                    entry->write_date  = ((uint16)directoryEntry->WrtDate[1] << 8) | directoryEntry->WrtDate[0];
+#endif
+
+                    entry->size = FAT_HTONL(directoryEntry->FileSize);
+                    entry->cluster = (FAT_HTONS(directoryEntry->FstClusHI)<<16) | FAT_HTONS(directoryEntry->FstClusLO);
+
+                    // Next starting position
+                    dirls->offset = item + 1;
+                    result = 1;
+                    return 1;
+                }
+            }// end of for
+
+            // If reached end of the dir move onto next sector
+            dirls->sector++;
+            dirls->offset = 0;
+        }
+        else
+            break;
+    }
+
+    return result;
+}
+#endif
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_access.h b/vtoyjump/vtoyjump/fat_io_lib/fat_access.h
new file mode 100644 (file)
index 0000000..1752387
--- /dev/null
@@ -0,0 +1,133 @@
+#ifndef __FAT_ACCESS_H__
+#define __FAT_ACCESS_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define FAT_INIT_OK                         0
+#define FAT_INIT_MEDIA_ACCESS_ERROR         (-1)
+#define FAT_INIT_INVALID_SECTOR_SIZE        (-2)
+#define FAT_INIT_INVALID_SIGNATURE          (-3)
+#define FAT_INIT_ENDIAN_ERROR               (-4)
+#define FAT_INIT_WRONG_FILESYS_TYPE         (-5)
+#define FAT_INIT_WRONG_PARTITION_TYPE       (-6)
+#define FAT_INIT_STRUCT_PACKING             (-7)
+
+#define FAT_DIR_ENTRIES_PER_SECTOR          (FAT_SECTOR_SIZE / FAT_DIR_ENTRY_SIZE)
+
+//-----------------------------------------------------------------------------
+// Function Pointers
+//-----------------------------------------------------------------------------
+typedef int (*fn_diskio_read) (uint32 sector, uint8 *buffer, uint32 sector_count);
+typedef int (*fn_diskio_write)(uint32 sector, uint8 *buffer, uint32 sector_count);
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct disk_if
+{
+    // User supplied function pointers for disk IO
+    fn_diskio_read          read_media;
+    fn_diskio_write         write_media;
+};
+
+// Forward declaration
+struct fat_buffer;
+
+struct fat_buffer
+{
+    uint8                   sector[FAT_SECTOR_SIZE * FAT_BUFFER_SECTORS];
+    uint32                  address;
+    int                     dirty;
+    uint8 *                 ptr;
+
+    // Next in chain of sector buffers
+    struct fat_buffer       *next;
+};
+
+typedef enum eFatType
+{
+    FAT_TYPE_16,
+    FAT_TYPE_32
+} tFatType;
+
+struct fatfs
+{
+    // Filesystem globals
+    uint8                   sectors_per_cluster;
+    uint32                  cluster_begin_lba;
+    uint32                  rootdir_first_cluster;
+    uint32                  rootdir_first_sector;
+    uint32                  rootdir_sectors;
+    uint32                  fat_begin_lba;
+    uint16                  fs_info_sector;
+    uint32                  lba_begin;
+    uint32                  fat_sectors;
+    uint32                  next_free_cluster;
+    uint16                  root_entry_count;
+    uint16                  reserved_sectors;
+    uint8                   num_of_fats;
+    tFatType                fat_type;
+
+    // Disk/Media API
+    struct disk_if          disk_io;
+
+    // [Optional] Thread Safety
+    void                    (*fl_lock)(void);
+    void                    (*fl_unlock)(void);
+
+    // Working buffer
+    struct fat_buffer        currentsector;
+
+    // FAT Buffer
+    struct fat_buffer        *fat_buffer_head;
+    struct fat_buffer        fat_buffers[FAT_BUFFERS];
+};
+
+struct fs_dir_list_status
+{
+    uint32                  sector;
+    uint32                  cluster;
+    uint8                   offset;
+};
+
+struct fs_dir_ent
+{
+    char                    filename[FATFS_MAX_LONG_FILENAME];
+    uint8                   is_dir;
+    uint32                  cluster;
+    uint32                  size;
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+    uint16                  access_date;
+    uint16                  write_time;
+    uint16                  write_date;
+    uint16                  create_date;
+    uint16                  create_time;
+#endif
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int     fatfs_init(struct fatfs *fs);
+uint32  fatfs_lba_of_cluster(struct fatfs *fs, uint32 Cluster_Number);
+int     fatfs_sector_reader(struct fatfs *fs, uint32 Startcluster, uint32 offset, uint8 *target);
+int     fatfs_sector_read(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int     fatfs_sector_write(struct fatfs *fs, uint32 lba, uint8 *target, uint32 count);
+int     fatfs_read_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+int     fatfs_write_sector(struct fatfs *fs, uint32 cluster, uint32 sector, uint8 *target);
+void    fatfs_show_details(struct fatfs *fs);
+uint32  fatfs_get_root_cluster(struct fatfs *fs);
+uint32  fatfs_get_file_entry(struct fatfs *fs, uint32 Cluster, char *nametofind, struct fat_dir_entry *sfEntry);
+int     fatfs_sfn_exists(struct fatfs *fs, uint32 Cluster, char *shortname);
+int     fatfs_update_file_length(struct fatfs *fs, uint32 Cluster, char *shortname, uint32 fileLength);
+int     fatfs_mark_file_deleted(struct fatfs *fs, uint32 Cluster, char *shortname);
+void    fatfs_list_directory_start(struct fatfs *fs, struct fs_dir_list_status *dirls, uint32 StartCluster);
+int     fatfs_list_directory_next(struct fatfs *fs, struct fs_dir_list_status *dirls, struct fs_dir_ent *entry);
+int     fatfs_update_timestamps(struct fat_dir_entry *directoryEntry, int create, int modify, int access);
+
+#endif
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_cache.c b/vtoyjump/vtoyjump/fat_io_lib/fat_cache.c
new file mode 100644 (file)
index 0000000..16e8494
--- /dev/null
@@ -0,0 +1,91 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//                            FAT16/32 File IO Library
+//                                    V2.6
+//                              Ultra-Embedded.com
+//                            Copyright 2003 - 2012
+//
+//                         Email: admin@ultra-embedded.com
+//
+//                                License: GPL
+//   If you would like a version with a more permissive license for use in
+//   closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include <string.h>
+#include "fat_cache.h"
+
+// Per file cluster chain caching used to improve performance.
+// This does not have to be enabled for architectures with low
+// memory space.
+
+//-----------------------------------------------------------------------------
+// fatfs_cache_init:
+//-----------------------------------------------------------------------------
+int fatfs_cache_init(struct fatfs *fs, FL_FILE *file)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+    int i;
+
+    for (i=0;i<FAT_CLUSTER_CACHE_ENTRIES;i++)
+    {
+        file->cluster_cache_idx[i] = 0xFFFFFFFF; // Not used
+        file->cluster_cache_data[i] = 0;
+    }
+#endif
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_cache_get_next_cluster:
+//-----------------------------------------------------------------------------
+int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+    uint32 slot = clusterIdx % FAT_CLUSTER_CACHE_ENTRIES;
+
+    if (file->cluster_cache_idx[slot] == clusterIdx)
+    {
+        *pNextCluster = file->cluster_cache_data[slot];
+        return 1;
+    }
+#endif
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_cache_set_next_cluster:
+//-----------------------------------------------------------------------------
+int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster)
+{
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+    uint32 slot = clusterIdx % FAT_CLUSTER_CACHE_ENTRIES;
+
+    if (file->cluster_cache_idx[slot] == clusterIdx)
+        file->cluster_cache_data[slot] = nextCluster;
+    else
+    {
+        file->cluster_cache_idx[slot] = clusterIdx;
+        file->cluster_cache_data[slot] = nextCluster;
+    }
+#endif
+
+    return 1;
+}
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_cache.h b/vtoyjump/vtoyjump/fat_io_lib/fat_cache.h
new file mode 100644 (file)
index 0000000..348d5d3
--- /dev/null
@@ -0,0 +1,13 @@
+#ifndef __FAT_CACHE_H__
+#define __FAT_CACHE_H__
+
+#include "fat_filelib.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_cache_init(struct fatfs *fs, FL_FILE *file);
+int fatfs_cache_get_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 *pNextCluster);
+int fatfs_cache_set_next_cluster(struct fatfs *fs, FL_FILE *file, uint32 clusterIdx, uint32 nextCluster);
+
+#endif
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_defs.h b/vtoyjump/vtoyjump/fat_io_lib/fat_defs.h
new file mode 100644 (file)
index 0000000..5fe8d6a
--- /dev/null
@@ -0,0 +1,128 @@
+#ifndef __FAT_DEFS_H__
+#define __FAT_DEFS_H__
+
+#include "fat_opts.h"
+#include "fat_types.h"
+
+//-----------------------------------------------------------------------------
+//            FAT32 Offsets
+//        Name                Offset
+//-----------------------------------------------------------------------------
+
+// Boot Sector
+#define BS_JMPBOOT              0    // Length = 3
+#define BS_OEMNAME              3    // Length = 8
+#define BPB_BYTSPERSEC          11    // Length = 2
+#define BPB_SECPERCLUS          13    // Length = 1
+#define BPB_RSVDSECCNT          14    // Length = 2
+#define BPB_NUMFATS             16    // Length = 1
+#define BPB_ROOTENTCNT          17    // Length = 2
+#define BPB_TOTSEC16            19    // Length = 2
+#define BPB_MEDIA               21    // Length = 1
+#define    BPB_FATSZ16          22    // Length = 2
+#define BPB_SECPERTRK           24    // Length = 2
+#define BPB_NUMHEADS            26    // Length = 2
+#define BPB_HIDDSEC             28    // Length = 4
+#define BPB_TOTSEC32            32    // Length = 4
+
+// FAT 12/16
+#define BS_FAT_DRVNUM           36    // Length = 1
+#define BS_FAT_BOOTSIG          38    // Length = 1
+#define BS_FAT_VOLID            39    // Length = 4
+#define BS_FAT_VOLLAB           43    // Length = 11
+#define BS_FAT_FILSYSTYPE       54    // Length = 8
+
+// FAT 32
+#define BPB_FAT32_FATSZ32       36    // Length = 4
+#define BPB_FAT32_EXTFLAGS      40    // Length = 2
+#define BPB_FAT32_FSVER         42    // Length = 2
+#define BPB_FAT32_ROOTCLUS      44    // Length = 4
+#define BPB_FAT32_FSINFO        48    // Length = 2
+#define BPB_FAT32_BKBOOTSEC     50    // Length = 2
+#define BS_FAT32_DRVNUM         64    // Length = 1
+#define BS_FAT32_BOOTSIG        66    // Length = 1
+#define BS_FAT32_VOLID          67    // Length = 4
+#define BS_FAT32_VOLLAB         71    // Length = 11
+#define BS_FAT32_FILSYSTYPE     82    // Length = 8
+
+//-----------------------------------------------------------------------------
+// FAT Types
+//-----------------------------------------------------------------------------
+#define FAT_TYPE_FAT12          1
+#define FAT_TYPE_FAT16          2
+#define FAT_TYPE_FAT32          3
+
+//-----------------------------------------------------------------------------
+// FAT32 Specific Statics
+//-----------------------------------------------------------------------------
+#define SIGNATURE_POSITION              510
+#define SIGNATURE_VALUE                 0xAA55
+#define PARTITION1_TYPECODE_LOCATION    450
+#define FAT32_TYPECODE1                 0x0B
+#define FAT32_TYPECODE2                 0x0C
+#define PARTITION1_LBA_BEGIN_LOCATION   454
+#define PARTITION1_SIZE_LOCATION        458
+
+#define FAT_DIR_ENTRY_SIZE              32
+#define FAT_SFN_SIZE_FULL               11
+#define FAT_SFN_SIZE_PARTIAL            8
+
+//-----------------------------------------------------------------------------
+// FAT32 File Attributes and Types
+//-----------------------------------------------------------------------------
+#define FILE_ATTR_READ_ONLY             0x01
+#define FILE_ATTR_HIDDEN                0x02
+#define FILE_ATTR_SYSTEM                0x04
+#define FILE_ATTR_SYSHID                0x06
+#define FILE_ATTR_VOLUME_ID             0x08
+#define FILE_ATTR_DIRECTORY             0x10
+#define FILE_ATTR_ARCHIVE               0x20
+#define FILE_ATTR_LFN_TEXT              0x0F
+#define FILE_HEADER_BLANK               0x00
+#define FILE_HEADER_DELETED             0xE5
+#define FILE_TYPE_DIR                   0x10
+#define FILE_TYPE_FILE                  0x20
+
+//-----------------------------------------------------------------------------
+// Time / Date details
+//-----------------------------------------------------------------------------
+#define FAT_TIME_HOURS_SHIFT            11
+#define FAT_TIME_HOURS_MASK             0x1F
+#define FAT_TIME_MINUTES_SHIFT          5
+#define FAT_TIME_MINUTES_MASK           0x3F
+#define FAT_TIME_SECONDS_SHIFT          0
+#define FAT_TIME_SECONDS_MASK           0x1F
+#define FAT_TIME_SECONDS_SCALE          2
+#define FAT_DATE_YEAR_SHIFT             9
+#define FAT_DATE_YEAR_MASK              0x7F
+#define FAT_DATE_MONTH_SHIFT            5
+#define FAT_DATE_MONTH_MASK             0xF
+#define FAT_DATE_DAY_SHIFT              0
+#define FAT_DATE_DAY_MASK               0x1F
+#define FAT_DATE_YEAR_OFFSET            1980
+
+//-----------------------------------------------------------------------------
+// Other Defines
+//-----------------------------------------------------------------------------
+#define FAT32_LAST_CLUSTER              0xFFFFFFFF
+#define FAT32_INVALID_CLUSTER           0xFFFFFFFF
+
+STRUCT_PACK_BEGIN
+struct fat_dir_entry STRUCT_PACK
+{
+    uint8 Name[11];
+    uint8 Attr;
+    uint8 NTRes;
+    uint8 CrtTimeTenth;
+    uint8 CrtTime[2];
+    uint8 CrtDate[2];
+    uint8 LstAccDate[2];
+    uint16 FstClusHI;
+    uint8 WrtTime[2];
+    uint8 WrtDate[2];
+    uint16 FstClusLO;
+    uint32 FileSize;
+} STRUCT_PACKED;
+STRUCT_PACK_END
+
+#endif
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_filelib.c b/vtoyjump/vtoyjump/fat_io_lib/fat_filelib.c
new file mode 100644 (file)
index 0000000..567ac55
--- /dev/null
@@ -0,0 +1,1603 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//                            FAT16/32 File IO Library
+//                                    V2.6
+//                              Ultra-Embedded.com
+//                            Copyright 2003 - 2012
+//
+//                         Email: admin@ultra-embedded.com
+//
+//                                License: GPL
+//   If you would like a version with a more permissive license for use in
+//   closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include <stdlib.h>
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_misc.h"
+#include "fat_string.h"
+#include "fat_filelib.h"
+#include "fat_cache.h"
+
+//-----------------------------------------------------------------------------
+// Locals
+//-----------------------------------------------------------------------------
+static FL_FILE            _files[FATFS_MAX_OPEN_FILES];
+static int                _filelib_init = 0;
+static int                _filelib_valid = 0;
+static struct fatfs       _fs;
+static struct fat_list    _open_file_list;
+static struct fat_list    _free_file_list;
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+
+// Macro for checking if file lib is initialised
+#define CHECK_FL_INIT()     { if (_filelib_init==0) fl_init(); }
+
+#define FL_LOCK(a)          do { if ((a)->fl_lock) (a)->fl_lock(); } while (0)
+#define FL_UNLOCK(a)        do { if ((a)->fl_unlock) (a)->fl_unlock(); } while (0)
+
+//-----------------------------------------------------------------------------
+// Local Functions
+//-----------------------------------------------------------------------------
+static void                _fl_init();
+
+//-----------------------------------------------------------------------------
+// _allocate_file: Find a slot in the open files buffer for a new file
+//-----------------------------------------------------------------------------
+static FL_FILE* _allocate_file(void)
+{
+    // Allocate free file
+    struct fat_node *node = fat_list_pop_head(&_free_file_list);
+
+    // Add to open list
+    if (node)
+        fat_list_insert_last(&_open_file_list, node);
+
+    return fat_list_entry(node, FL_FILE, list_node);
+}
+//-----------------------------------------------------------------------------
+// _check_file_open: Returns true if the file is already open
+//-----------------------------------------------------------------------------
+static int _check_file_open(FL_FILE* file)
+{
+    struct fat_node *node;
+
+    // Compare open files
+    fat_list_for_each(&_open_file_list, node)
+    {
+        FL_FILE* openFile = fat_list_entry(node, FL_FILE, list_node);
+
+        // If not the current file
+        if (openFile != file)
+        {
+            // Compare path and name
+            if ( (fatfs_compare_names(openFile->path,file->path)) && (fatfs_compare_names(openFile->filename,file->filename)) )
+                return 1;
+        }
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// _free_file: Free open file handle
+//-----------------------------------------------------------------------------
+static void _free_file(FL_FILE* file)
+{
+    // Remove from open list
+    fat_list_remove(&_open_file_list, &file->list_node);
+
+    // Add to free list
+    fat_list_insert_last(&_free_file_list, &file->list_node);
+}
+
+//-----------------------------------------------------------------------------
+//                                Low Level
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// _open_directory: Cycle through path string to find the start cluster
+// address of the highest subdir.
+//-----------------------------------------------------------------------------
+static int _open_directory(char *path, uint32 *pathCluster)
+{
+    int levels;
+    int sublevel;
+    char currentfolder[FATFS_MAX_LONG_FILENAME];
+    struct fat_dir_entry sfEntry;
+    uint32 startcluster;
+
+    // Set starting cluster to root cluster
+    startcluster = fatfs_get_root_cluster(&_fs);
+
+    // Find number of levels
+    levels = fatfs_total_path_levels(path);
+
+    // Cycle through each level and get the start sector
+    for (sublevel=0;sublevel<(levels+1);sublevel++)
+    {
+        if (fatfs_get_substring(path, sublevel, currentfolder, sizeof(currentfolder)) == -1)
+            return 0;
+
+        // Find clusteraddress for folder (currentfolder)
+        if (fatfs_get_file_entry(&_fs, startcluster, currentfolder,&sfEntry))
+        {
+            // Check entry is folder
+            if (fatfs_entry_is_dir(&sfEntry))
+                startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
+            else
+                return 0;
+        }
+        else
+            return 0;
+    }
+
+    *pathCluster = startcluster;
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// _create_directory: Cycle through path string and create the end directory
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static int _create_directory(char *path)
+{
+    FL_FILE* file;
+    struct fat_dir_entry sfEntry;
+    char shortFilename[FAT_SFN_SIZE_FULL];
+    int tailNum = 0;
+    int i;
+
+    // Allocate a new file handle
+    file = _allocate_file();
+    if (!file)
+        return 0;
+
+    // Clear filename
+    memset(file->path, '\0', sizeof(file->path));
+    memset(file->filename, '\0', sizeof(file->filename));
+
+    // Split full path into filename and directory path
+    if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+    {
+        _free_file(file);
+        return 0;
+    }
+
+    // Check if file already open
+    if (_check_file_open(file))
+    {
+        _free_file(file);
+        return 0;
+    }
+
+    // If file is in the root dir
+    if (file->path[0] == 0)
+        file->parentcluster = fatfs_get_root_cluster(&_fs);
+    else
+    {
+        // Find parent directory start cluster
+        if (!_open_directory(file->path, &file->parentcluster))
+        {
+            _free_file(file);
+            return 0;
+        }
+    }
+
+    // Check if same filename exists in directory
+    if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1)
+    {
+        _free_file(file);
+        return 0;
+    }
+
+    file->startcluster = 0;
+
+    // Create the file space for the folder (at least one clusters worth!)
+    if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1))
+    {
+        _free_file(file);
+        return 0;
+    }
+
+    // Erase new directory cluster
+    memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
+    for (i=0;i<_fs.sectors_per_cluster;i++)
+    {
+        if (!fatfs_write_sector(&_fs, file->startcluster, i, file->file_data_sector))
+        {
+            _free_file(file);
+            return 0;
+        }
+    }
+
+#if FATFS_INC_LFN_SUPPORT
+
+    // Generate a short filename & tail
+    tailNum = 0;
+    do
+    {
+        // Create a standard short filename (without tail)
+        fatfs_lfn_create_sfn(shortFilename, file->filename);
+
+        // If second hit or more, generate a ~n tail
+        if (tailNum != 0)
+            fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
+        // Try with no tail if first entry
+        else
+            memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+        // Check if entry exists already or not
+        if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0)
+            break;
+
+        tailNum++;
+    }
+    while (tailNum < 9999);
+
+    // We reached the max number of duplicate short file names (unlikely!)
+    if (tailNum == 9999)
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return 0;
+    }
+#else
+    // Create a standard short filename (without tail)
+    if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return 0;
+    }
+
+    // Copy to SFN space
+    memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+    // Check if entry exists already
+    if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return 0;
+    }
+#endif
+
+    // Add file to disk
+    if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 1))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return 0;
+    }
+
+    // General
+    file->filelength = 0;
+    file->bytenum = 0;
+    file->file_data_address = 0xFFFFFFFF;
+    file->file_data_dirty = 0;
+    file->filelength_changed = 0;
+
+    // Quick lookup for next link in the chain
+    file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+    file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+    fatfs_fat_purge(&_fs);
+
+    _free_file(file);
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// _open_file: Open a file for reading
+//-----------------------------------------------------------------------------
+static FL_FILE* _open_file(const char *path)
+{
+    FL_FILE* file;
+    struct fat_dir_entry sfEntry;
+
+    // Allocate a new file handle
+    file = _allocate_file();
+    if (!file)
+        return NULL;
+
+    // Clear filename
+    memset(file->path, '\0', sizeof(file->path));
+    memset(file->filename, '\0', sizeof(file->filename));
+
+    // Split full path into filename and directory path
+    if (fatfs_split_path((char*)path, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    // Check if file already open
+    if (_check_file_open(file))
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    // If file is in the root dir
+    if (file->path[0]==0)
+        file->parentcluster = fatfs_get_root_cluster(&_fs);
+    else
+    {
+        // Find parent directory start cluster
+        if (!_open_directory(file->path, &file->parentcluster))
+        {
+            _free_file(file);
+            return NULL;
+        }
+    }
+
+    // Using dir cluster address search for filename
+    if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry))
+        // Make sure entry is file not dir!
+        if (fatfs_entry_is_file(&sfEntry))
+        {
+            // Initialise file details
+            memcpy(file->shortfilename, sfEntry.Name, FAT_SFN_SIZE_FULL);
+            file->filelength = FAT_HTONL(sfEntry.FileSize);
+            file->bytenum = 0;
+            file->startcluster = ((FAT_HTONS((uint32)sfEntry.FstClusHI))<<16) + FAT_HTONS(sfEntry.FstClusLO);
+            file->file_data_address = 0xFFFFFFFF;
+            file->file_data_dirty = 0;
+            file->filelength_changed = 0;
+
+            // Quick lookup for next link in the chain
+            file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+            file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+            fatfs_cache_init(&_fs, file);
+
+            fatfs_fat_purge(&_fs);
+
+            return file;
+        }
+
+    _free_file(file);
+    return NULL;
+}
+//-----------------------------------------------------------------------------
+// _create_file: Create a new file
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static FL_FILE* _create_file(const char *filename)
+{
+    FL_FILE* file;
+    struct fat_dir_entry sfEntry;
+    char shortFilename[FAT_SFN_SIZE_FULL];
+    int tailNum = 0;
+
+    // No write access?
+    if (!_fs.disk_io.write_media)
+        return NULL;
+
+    // Allocate a new file handle
+    file = _allocate_file();
+    if (!file)
+        return NULL;
+
+    // Clear filename
+    memset(file->path, '\0', sizeof(file->path));
+    memset(file->filename, '\0', sizeof(file->filename));
+
+    // Split full path into filename and directory path
+    if (fatfs_split_path((char*)filename, file->path, sizeof(file->path), file->filename, sizeof(file->filename)) == -1)
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    // Check if file already open
+    if (_check_file_open(file))
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    // If file is in the root dir
+    if (file->path[0] == 0)
+        file->parentcluster = fatfs_get_root_cluster(&_fs);
+    else
+    {
+        // Find parent directory start cluster
+        if (!_open_directory(file->path, &file->parentcluster))
+        {
+            _free_file(file);
+            return NULL;
+        }
+    }
+
+    // Check if same filename exists in directory
+    if (fatfs_get_file_entry(&_fs, file->parentcluster, file->filename,&sfEntry) == 1)
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+    file->startcluster = 0;
+
+    // Create the file space for the file (at least one clusters worth!)
+    if (!fatfs_allocate_free_space(&_fs, 1, &file->startcluster, 1))
+    {
+        _free_file(file);
+        return NULL;
+    }
+
+#if FATFS_INC_LFN_SUPPORT
+    // Generate a short filename & tail
+    tailNum = 0;
+    do
+    {
+        // Create a standard short filename (without tail)
+        fatfs_lfn_create_sfn(shortFilename, file->filename);
+
+        // If second hit or more, generate a ~n tail
+        if (tailNum != 0)
+            fatfs_lfn_generate_tail((char*)file->shortfilename, shortFilename, tailNum);
+        // Try with no tail if first entry
+        else
+            memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+        // Check if entry exists already or not
+        if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename) == 0)
+            break;
+
+        tailNum++;
+    }
+    while (tailNum < 9999);
+
+    // We reached the max number of duplicate short file names (unlikely!)
+    if (tailNum == 9999)
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return NULL;
+    }
+#else
+    // Create a standard short filename (without tail)
+    if (!fatfs_lfn_create_sfn(shortFilename, file->filename))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return NULL;
+    }
+
+    // Copy to SFN space
+    memcpy(file->shortfilename, shortFilename, FAT_SFN_SIZE_FULL);
+
+    // Check if entry exists already
+    if (fatfs_sfn_exists(&_fs, file->parentcluster, (char*)file->shortfilename))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return NULL;
+    }
+#endif
+
+    // Add file to disk
+    if (!fatfs_add_file_entry(&_fs, file->parentcluster, (char*)file->filename, (char*)file->shortfilename, file->startcluster, 0, 0))
+    {
+        // Delete allocated space
+        fatfs_free_cluster_chain(&_fs, file->startcluster);
+
+        _free_file(file);
+        return NULL;
+    }
+
+    // General
+    file->filelength = 0;
+    file->bytenum = 0;
+    file->file_data_address = 0xFFFFFFFF;
+    file->file_data_dirty = 0;
+    file->filelength_changed = 0;
+
+    // Quick lookup for next link in the chain
+    file->last_fat_lookup.ClusterIdx = 0xFFFFFFFF;
+    file->last_fat_lookup.CurrentCluster = 0xFFFFFFFF;
+
+    fatfs_cache_init(&_fs, file);
+
+    fatfs_fat_purge(&_fs);
+
+    return file;
+}
+#endif
+//-----------------------------------------------------------------------------
+// _read_sectors: Read sector(s) from disk to file
+//-----------------------------------------------------------------------------
+static uint32 _read_sectors(FL_FILE* file, uint32 offset, uint8 *buffer, uint32 count)
+{
+    uint32 Sector = 0;
+    uint32 ClusterIdx = 0;
+    uint32 Cluster = 0;
+    uint32 i;
+    uint32 lba;
+
+    // Find cluster index within file & sector with cluster
+    ClusterIdx = offset / _fs.sectors_per_cluster;
+    Sector = offset - (ClusterIdx * _fs.sectors_per_cluster);
+
+    // Limit number of sectors read to the number remaining in this cluster
+    if ((Sector + count) > _fs.sectors_per_cluster)
+        count = _fs.sectors_per_cluster - Sector;
+
+    // Quick lookup for next link in the chain
+    if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
+        Cluster = file->last_fat_lookup.CurrentCluster;
+    // Else walk the chain
+    else
+    {
+        // Starting from last recorded cluster?
+        if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
+        {
+            i = file->last_fat_lookup.ClusterIdx;
+            Cluster = file->last_fat_lookup.CurrentCluster;
+        }
+        // Start searching from the beginning..
+        else
+        {
+            // Set start of cluster chain to initial value
+            i = 0;
+            Cluster = file->startcluster;
+        }
+
+        // Follow chain to find cluster to read
+        for ( ;i<ClusterIdx; i++)
+        {
+            uint32 nextCluster;
+
+            // Does the entry exist in the cache?
+            if (!fatfs_cache_get_next_cluster(&_fs, file, i, &nextCluster))
+            {
+                // Scan file linked list to find next entry
+                nextCluster = fatfs_find_next_cluster(&_fs, Cluster);
+
+                // Push entry into cache
+                fatfs_cache_set_next_cluster(&_fs, file, i, nextCluster);
+            }
+
+            Cluster = nextCluster;
+        }
+
+        // Record current cluster lookup details (if valid)
+        if (Cluster != FAT32_LAST_CLUSTER)
+        {
+            file->last_fat_lookup.CurrentCluster = Cluster;
+            file->last_fat_lookup.ClusterIdx = ClusterIdx;
+        }
+    }
+
+    // If end of cluster chain then return false
+    if (Cluster == FAT32_LAST_CLUSTER)
+        return 0;
+
+    // Calculate sector address
+    lba = fatfs_lba_of_cluster(&_fs, Cluster) + Sector;
+
+    // Read sector of file
+    if (fatfs_sector_read(&_fs, lba, buffer, count))
+        return count;
+    else
+        return 0;
+}
+
+//-----------------------------------------------------------------------------
+//                                External API
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// fl_init: Initialise library
+//-----------------------------------------------------------------------------
+void fl_init(void)
+{
+    int i;
+
+    fat_list_init(&_free_file_list);
+    fat_list_init(&_open_file_list);
+
+    // Add all file objects to free list
+    for (i=0;i<FATFS_MAX_OPEN_FILES;i++)
+        fat_list_insert_last(&_free_file_list, &_files[i].list_node);
+
+    _filelib_init = 1;
+}
+//-----------------------------------------------------------------------------
+// fl_attach_locks:
+//-----------------------------------------------------------------------------
+void fl_attach_locks(void (*lock)(void), void (*unlock)(void))
+{
+    _fs.fl_lock = lock;
+    _fs.fl_unlock = unlock;
+}
+//-----------------------------------------------------------------------------
+// fl_attach_media:
+//-----------------------------------------------------------------------------
+int fl_attach_media(fn_diskio_read rd, fn_diskio_write wr)
+{
+    int res;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    _fs.disk_io.read_media = rd;
+    _fs.disk_io.write_media = wr;
+
+    // Initialise FAT parameters
+    if ((res = fatfs_init(&_fs)) != FAT_INIT_OK)
+    {
+        FAT_PRINTF(("FAT_FS: Error could not load FAT details (%d)!\r\n", res));
+        return res;
+    }
+
+    _filelib_valid = 1;
+    return FAT_INIT_OK;
+}
+//-----------------------------------------------------------------------------
+// fl_shutdown: Call before shutting down system
+//-----------------------------------------------------------------------------
+void fl_shutdown(void)
+{
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+    fatfs_fat_purge(&_fs);
+    FL_UNLOCK(&_fs);
+}
+//-----------------------------------------------------------------------------
+// fopen: Open or Create a file for reading or writing
+//-----------------------------------------------------------------------------
+void* fl_fopen(const char *path, const char *mode)
+{
+    int i;
+    FL_FILE* file;
+    uint8 flags = 0;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (!_filelib_valid)
+        return NULL;
+
+    if (!path || !mode)
+        return NULL;
+
+    // Supported Modes:
+    // "r" Open a file for reading.
+    //        The file must exist.
+    // "w" Create an empty file for writing.
+    //        If a file with the same name already exists its content is erased and the file is treated as a new empty file.
+    // "a" Append to a file.
+    //        Writing operations append data at the end of the file.
+    //        The file is created if it does not exist.
+    // "r+" Open a file for update both reading and writing.
+    //        The file must exist.
+    // "w+" Create an empty file for both reading and writing.
+    //        If a file with the same name already exists its content is erased and the file is treated as a new empty file.
+    // "a+" Open a file for reading and appending.
+    //        All writing operations are performed at the end of the file, protecting the previous content to be overwritten.
+    //        You can reposition (fseek, rewind) the internal pointer to anywhere in the file for reading, but writing operations
+    //        will move it back to the end of file.
+    //        The file is created if it does not exist.
+
+    for (i=0;i<(int)strlen(mode);i++)
+    {
+        switch (mode[i])
+        {
+        case 'r':
+        case 'R':
+            flags |= FILE_READ;
+            break;
+        case 'w':
+        case 'W':
+            flags |= FILE_WRITE;
+            flags |= FILE_ERASE;
+            flags |= FILE_CREATE;
+            break;
+        case 'a':
+        case 'A':
+            flags |= FILE_WRITE;
+            flags |= FILE_APPEND;
+            flags |= FILE_CREATE;
+            break;
+        case '+':
+            if (flags & FILE_READ)
+                flags |= FILE_WRITE;
+            else if (flags & FILE_WRITE)
+            {
+                flags |= FILE_READ;
+                flags |= FILE_ERASE;
+                flags |= FILE_CREATE;
+            }
+            else if (flags & FILE_APPEND)
+            {
+                flags |= FILE_READ;
+                flags |= FILE_WRITE;
+                flags |= FILE_APPEND;
+                flags |= FILE_CREATE;
+            }
+            break;
+        case 'b':
+        case 'B':
+            flags |= FILE_BINARY;
+            break;
+        }
+    }
+
+    file = NULL;
+
+#if FATFS_INC_WRITE_SUPPORT == 0
+    // No write support!
+    flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
+#endif
+
+    // No write access - remove write/modify flags
+    if (!_fs.disk_io.write_media)
+        flags &= ~(FILE_CREATE | FILE_WRITE | FILE_APPEND);
+
+    FL_LOCK(&_fs);
+
+    // Read
+    if (flags & FILE_READ)
+        file = _open_file(path);
+
+    // Create New
+#if FATFS_INC_WRITE_SUPPORT
+    if (!file && (flags & FILE_CREATE))
+        file = _create_file(path);
+#endif
+
+    // Write Existing (and not open due to read or create)
+    if (!(flags & FILE_READ))
+        if ((flags & FILE_CREATE) && !file)
+            if (flags & (FILE_WRITE | FILE_APPEND))
+                file = _open_file(path);
+
+    if (file)
+        file->flags = flags;
+
+    FL_UNLOCK(&_fs);
+    return file;
+}
+//-----------------------------------------------------------------------------
+// _write_sectors: Write sector(s) to disk
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+static uint32 _write_sectors(FL_FILE* file, uint32 offset, uint8 *buf, uint32 count)
+{
+    uint32 SectorNumber = 0;
+    uint32 ClusterIdx = 0;
+    uint32 Cluster = 0;
+    uint32 LastCluster = FAT32_LAST_CLUSTER;
+    uint32 i;
+    uint32 lba;
+    uint32 TotalWriteCount = count;
+
+    // Find values for Cluster index & sector within cluster
+    ClusterIdx = offset / _fs.sectors_per_cluster;
+    SectorNumber = offset - (ClusterIdx * _fs.sectors_per_cluster);
+
+    // Limit number of sectors written to the number remaining in this cluster
+    if ((SectorNumber + count) > _fs.sectors_per_cluster)
+        count = _fs.sectors_per_cluster - SectorNumber;
+
+    // Quick lookup for next link in the chain
+    if (ClusterIdx == file->last_fat_lookup.ClusterIdx)
+        Cluster = file->last_fat_lookup.CurrentCluster;
+    // Else walk the chain
+    else
+    {
+        // Starting from last recorded cluster?
+        if (ClusterIdx && ClusterIdx == file->last_fat_lookup.ClusterIdx + 1)
+        {
+            i = file->last_fat_lookup.ClusterIdx;
+            Cluster = file->last_fat_lookup.CurrentCluster;
+        }
+        // Start searching from the beginning..
+        else
+        {
+            // Set start of cluster chain to initial value
+            i = 0;
+            Cluster = file->startcluster;
+        }
+
+        // Follow chain to find cluster to read
+        for ( ;i<ClusterIdx; i++)
+        {
+            uint32 nextCluster;
+
+            // Does the entry exist in the cache?
+            if (!fatfs_cache_get_next_cluster(&_fs, file, i, &nextCluster))
+            {
+                // Scan file linked list to find next entry
+                nextCluster = fatfs_find_next_cluster(&_fs, Cluster);
+
+                // Push entry into cache
+                fatfs_cache_set_next_cluster(&_fs, file, i, nextCluster);
+            }
+
+            LastCluster = Cluster;
+            Cluster = nextCluster;
+
+            // Dont keep following a dead end
+            if (Cluster == FAT32_LAST_CLUSTER)
+                break;
+        }
+
+        // If we have reached the end of the chain, allocate more!
+        if (Cluster == FAT32_LAST_CLUSTER)
+        {
+            // Add some more cluster(s) to the last good cluster chain
+            if (!fatfs_add_free_space(&_fs, &LastCluster,  (TotalWriteCount + _fs.sectors_per_cluster -1) / _fs.sectors_per_cluster))
+                return 0;
+
+            Cluster = LastCluster;
+        }
+
+        // Record current cluster lookup details
+        file->last_fat_lookup.CurrentCluster = Cluster;
+        file->last_fat_lookup.ClusterIdx = ClusterIdx;
+    }
+
+    // Calculate write address
+    lba = fatfs_lba_of_cluster(&_fs, Cluster) + SectorNumber;
+
+    if (fatfs_sector_write(&_fs, lba, buf, count))
+        return count;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fflush: Flush un-written data to the file
+//-----------------------------------------------------------------------------
+int fl_fflush(void *f)
+{
+#if FATFS_INC_WRITE_SUPPORT
+    FL_FILE *file = (FL_FILE *)f;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (file)
+    {
+        FL_LOCK(&_fs);
+
+        // If some write data still in buffer
+        if (file->file_data_dirty)
+        {
+            // Write back current sector before loading next
+            if (_write_sectors(file, file->file_data_address, file->file_data_sector, 1))
+                file->file_data_dirty = 0;
+        }
+
+        FL_UNLOCK(&_fs);
+    }
+#endif
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fl_fclose: Close an open file
+//-----------------------------------------------------------------------------
+void fl_fclose(void *f)
+{
+    FL_FILE *file = (FL_FILE *)f;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (file)
+    {
+        FL_LOCK(&_fs);
+
+        // Flush un-written data to file
+        fl_fflush(f);
+
+        // File size changed?
+        if (file->filelength_changed)
+        {
+#if FATFS_INC_WRITE_SUPPORT
+            // Update filesize in directory
+            fatfs_update_file_length(&_fs, file->parentcluster, (char*)file->shortfilename, file->filelength);
+#endif
+            file->filelength_changed = 0;
+        }
+
+        file->bytenum = 0;
+        file->filelength = 0;
+        file->startcluster = 0;
+        file->file_data_address = 0xFFFFFFFF;
+        file->file_data_dirty = 0;
+        file->filelength_changed = 0;
+
+        // Free file handle
+        _free_file(file);
+
+        fatfs_fat_purge(&_fs);
+
+        FL_UNLOCK(&_fs);
+    }
+}
+//-----------------------------------------------------------------------------
+// fl_fgetc: Get a character in the stream
+//-----------------------------------------------------------------------------
+int fl_fgetc(void *f)
+{
+    int res;
+    uint8 data = 0;
+
+    res = fl_fread(&data, 1, 1, f);
+    if (res == 1)
+        return (int)data;
+    else
+        return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fgets: Get a string from a stream
+//-----------------------------------------------------------------------------
+char *fl_fgets(char *s, int n, void *f)
+{
+    int idx = 0;
+
+    // Space for null terminator?
+    if (n > 0)
+    {
+        // While space (+space for null terminator)
+        while (idx < (n-1))
+        {
+            int ch = fl_fgetc(f);
+
+            // EOF / Error?
+            if (ch < 0)
+                break;
+
+            // Store character read from stream
+            s[idx++] = (char)ch;
+
+            // End of line?
+            if (ch == '\n')
+                break;
+        }
+
+        if (idx > 0)
+            s[idx] = '\0';
+    }
+
+    return (idx > 0) ? s : 0;
+}
+//-----------------------------------------------------------------------------
+// fl_fread: Read a block of data from the file
+//-----------------------------------------------------------------------------
+int fl_fread(void * buffer, int size, int length, void *f )
+{
+    uint32 sector;
+    uint32 offset;
+    int copyCount;
+    int count = size * length;
+    int bytesRead = 0;
+
+    FL_FILE *file = (FL_FILE *)f;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (buffer==NULL || file==NULL)
+        return -1;
+
+    // No read permissions
+    if (!(file->flags & FILE_READ))
+        return -1;
+
+    // Nothing to be done
+    if (!count)
+        return 0;
+
+    // Check if read starts past end of file
+    if (file->bytenum >= file->filelength)
+        return -1;
+
+    // Limit to file size
+    if ( (file->bytenum + count) > file->filelength )
+        count = file->filelength - file->bytenum;
+
+    // Calculate start sector
+    sector = file->bytenum / FAT_SECTOR_SIZE;
+
+    // Offset to start copying data from first sector
+    offset = file->bytenum % FAT_SECTOR_SIZE;
+
+    while (bytesRead < count)
+    {
+        // Read whole sector, read from media directly into target buffer
+        if ((offset == 0) && ((count - bytesRead) >= FAT_SECTOR_SIZE))
+        {
+            // Read as many sectors as possible into target buffer
+            uint32 sectorsRead = _read_sectors(file, sector, (uint8*)((uint8*)buffer + bytesRead), (count - bytesRead) / FAT_SECTOR_SIZE);
+            if (sectorsRead)
+            {
+                // We have upto one sector to copy
+                copyCount = FAT_SECTOR_SIZE * sectorsRead;
+
+                // Move onto next sector and reset copy offset
+                sector+= sectorsRead;
+                offset = 0;
+            }
+            else
+                break;
+        }
+        else
+        {
+            // Do we need to re-read the sector?
+            if (file->file_data_address != sector)
+            {
+                // Flush un-written data to file
+                if (file->file_data_dirty)
+                    fl_fflush(file);
+
+                // Get LBA of sector offset within file
+                if (!_read_sectors(file, sector, file->file_data_sector, 1))
+                    // Read failed - out of range (probably)
+                    break;
+
+                file->file_data_address = sector;
+                file->file_data_dirty = 0;
+            }
+
+            // We have upto one sector to copy
+            copyCount = FAT_SECTOR_SIZE - offset;
+
+            // Only require some of this sector?
+            if (copyCount > (count - bytesRead))
+                copyCount = (count - bytesRead);
+
+            // Copy to application buffer
+            memcpy( (uint8*)((uint8*)buffer + bytesRead), (uint8*)(file->file_data_sector + offset), copyCount);
+
+            // Move onto next sector and reset copy offset
+            sector++;
+            offset = 0;
+        }
+
+        // Increase total read count
+        bytesRead += copyCount;
+
+        // Increment file pointer
+        file->bytenum += copyCount;
+    }
+
+    return bytesRead;
+}
+//-----------------------------------------------------------------------------
+// fl_fseek: Seek to a specific place in the file
+//-----------------------------------------------------------------------------
+int fl_fseek( void *f, long offset, int origin )
+{
+    FL_FILE *file = (FL_FILE *)f;
+    int res = -1;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (!file)
+        return -1;
+
+    if (origin == SEEK_END && offset != 0)
+        return -1;
+
+    FL_LOCK(&_fs);
+
+    // Invalidate file buffer
+    file->file_data_address = 0xFFFFFFFF;
+    file->file_data_dirty = 0;
+
+    if (origin == SEEK_SET)
+    {
+        file->bytenum = (uint32)offset;
+
+        if (file->bytenum > file->filelength)
+            file->bytenum = file->filelength;
+
+        res = 0;
+    }
+    else if (origin == SEEK_CUR)
+    {
+        // Positive shift
+        if (offset >= 0)
+        {
+            file->bytenum += offset;
+
+            if (file->bytenum > file->filelength)
+                file->bytenum = file->filelength;
+        }
+        // Negative shift
+        else
+        {
+            // Make shift positive
+            offset = -offset;
+
+            // Limit to negative shift to start of file
+            if ((uint32)offset > file->bytenum)
+                file->bytenum = 0;
+            else
+                file->bytenum-= offset;
+        }
+
+        res = 0;
+    }
+    else if (origin == SEEK_END)
+    {
+        file->bytenum = file->filelength;
+        res = 0;
+    }
+    else
+        res = -1;
+
+    FL_UNLOCK(&_fs);
+
+    return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fgetpos: Get the current file position
+//-----------------------------------------------------------------------------
+int fl_fgetpos(void *f , uint32 * position)
+{
+    FL_FILE *file = (FL_FILE *)f;
+
+    if (!file)
+        return -1;
+
+    FL_LOCK(&_fs);
+
+    // Get position
+    *position = file->bytenum;
+
+    FL_UNLOCK(&_fs);
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fl_ftell: Get the current file position
+//-----------------------------------------------------------------------------
+long fl_ftell(void *f)
+{
+    uint32 pos = 0;
+
+    fl_fgetpos(f, &pos);
+
+    return (long)pos;
+}
+//-----------------------------------------------------------------------------
+// fl_feof: Is the file pointer at the end of the stream?
+//-----------------------------------------------------------------------------
+int fl_feof(void *f)
+{
+    FL_FILE *file = (FL_FILE *)f;
+    int res;
+
+    if (!file)
+        return -1;
+
+    FL_LOCK(&_fs);
+
+    if (file->bytenum == file->filelength)
+        res = EOF;
+    else
+        res = 0;
+
+    FL_UNLOCK(&_fs);
+
+    return res;
+}
+//-----------------------------------------------------------------------------
+// fl_fputc: Write a character to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fputc(int c, void *f)
+{
+    uint8 data = (uint8)c;
+    int res;
+
+    res = fl_fwrite(&data, 1, 1, f);
+    if (res == 1)
+        return c;
+    else
+        return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fwrite: Write a block of data to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fwrite(const void * data, int size, int count, void *f )
+{
+    FL_FILE *file = (FL_FILE *)f;
+    uint32 sector;
+    uint32 offset;
+    uint32 length = (size*count);
+    uint8 *buffer = (uint8 *)data;
+    uint32 bytesWritten = 0;
+    uint32 copyCount;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    if (!file)
+        return -1;
+
+    FL_LOCK(&_fs);
+
+    // No write permissions
+    if (!(file->flags & FILE_WRITE))
+    {
+        FL_UNLOCK(&_fs);
+        return -1;
+    }
+
+    // Append writes to end of file
+    if (file->flags & FILE_APPEND)
+        file->bytenum = file->filelength;
+    // Else write to current position
+
+    // Calculate start sector
+    sector = file->bytenum / FAT_SECTOR_SIZE;
+
+    // Offset to start copying data from first sector
+    offset = file->bytenum % FAT_SECTOR_SIZE;
+
+    while (bytesWritten < length)
+    {
+        // Whole sector or more to be written?
+        if ((offset == 0) && ((length - bytesWritten) >= FAT_SECTOR_SIZE))
+        {
+            uint32 sectorsWrote;
+
+            // Buffered sector, flush back to disk
+            if (file->file_data_address != 0xFFFFFFFF)
+            {
+                // Flush un-written data to file
+                if (file->file_data_dirty)
+                    fl_fflush(file);
+
+                file->file_data_address = 0xFFFFFFFF;
+                file->file_data_dirty = 0;
+            }
+
+            // Write as many sectors as possible
+            sectorsWrote = _write_sectors(file, sector, (uint8*)(buffer + bytesWritten), (length - bytesWritten) / FAT_SECTOR_SIZE);
+            copyCount = FAT_SECTOR_SIZE * sectorsWrote;
+
+            // Increase total read count
+            bytesWritten += copyCount;
+
+            // Increment file pointer
+            file->bytenum += copyCount;
+
+            // Move onto next sector and reset copy offset
+            sector+= sectorsWrote;
+            offset = 0;
+
+            if (!sectorsWrote)
+                break;
+        }
+        else
+        {
+            // We have upto one sector to copy
+            copyCount = FAT_SECTOR_SIZE - offset;
+
+            // Only require some of this sector?
+            if (copyCount > (length - bytesWritten))
+                copyCount = (length - bytesWritten);
+
+            // Do we need to read a new sector?
+            if (file->file_data_address != sector)
+            {
+                // Flush un-written data to file
+                if (file->file_data_dirty)
+                    fl_fflush(file);
+
+                // If we plan to overwrite the whole sector, we don't need to read it first!
+                if (copyCount != FAT_SECTOR_SIZE)
+                {
+                    // NOTE: This does not have succeed; if last sector of file
+                    // reached, no valid data will be read in, but write will
+                    // allocate some more space for new data.
+
+                    // Get LBA of sector offset within file
+                    if (!_read_sectors(file, sector, file->file_data_sector, 1))
+                        memset(file->file_data_sector, 0x00, FAT_SECTOR_SIZE);
+                }
+
+                file->file_data_address = sector;
+                file->file_data_dirty = 0;
+            }
+
+            // Copy from application buffer into sector buffer
+            memcpy((uint8*)(file->file_data_sector + offset), (uint8*)(buffer + bytesWritten), copyCount);
+
+            // Mark buffer as dirty
+            file->file_data_dirty = 1;
+
+            // Increase total read count
+            bytesWritten += copyCount;
+
+            // Increment file pointer
+            file->bytenum += copyCount;
+
+            // Move onto next sector and reset copy offset
+            sector++;
+            offset = 0;
+        }
+    }
+
+    // Write increased extent of the file?
+    if (file->bytenum > file->filelength)
+    {
+        // Increase file size to new point
+        file->filelength = file->bytenum;
+
+        // We are changing the file length and this
+        // will need to be writen back at some point
+        file->filelength_changed = 1;
+    }
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+    // If time & date support is enabled, always force directory entry to be
+    // written in-order to update file modify / access time & date.
+    file->filelength_changed = 1;
+#endif
+
+    FL_UNLOCK(&_fs);
+
+    return (size*count);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_fputs: Write a character string to the stream
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_fputs(const char * str, void *f)
+{
+    int len = (int)strlen(str);
+    int res = fl_fwrite(str, 1, len, f);
+
+    if (res == len)
+        return len;
+    else
+        return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_remove: Remove a file from the filesystem
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_remove( const char * filename )
+{
+    FL_FILE* file;
+    int res = -1;
+
+    FL_LOCK(&_fs);
+
+    // Use read_file as this will check if the file is already open!
+    file = fl_fopen((char*)filename, "r");
+    if (file)
+    {
+        // Delete allocated space
+        if (fatfs_free_cluster_chain(&_fs, file->startcluster))
+        {
+            // Remove directory entries
+            if (fatfs_mark_file_deleted(&_fs, file->parentcluster, (char*)file->shortfilename))
+            {
+                // Close the file handle (this should not write anything to the file
+                // as we have not changed the file since opening it!)
+                fl_fclose(file);
+
+                res = 0;
+            }
+        }
+    }
+
+    FL_UNLOCK(&_fs);
+
+    return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_createdirectory: Create a directory based on a path
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fl_createdirectory(const char *path)
+{
+    int res;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+    res =_create_directory((char*)path);
+    FL_UNLOCK(&_fs);
+
+    return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_listdirectory: List a directory based on a path
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+void fl_listdirectory(const char *path)
+{
+    FL_DIR dirstat;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+
+    FAT_PRINTF(("\r\nDirectory %s\r\n", path));
+
+    if (fl_opendir(path, &dirstat))
+    {
+        struct fs_dir_ent dirent;
+
+        while (fl_readdir(&dirstat, &dirent) == 0)
+        {
+#if FATFS_INC_TIME_DATE_SUPPORT
+            int d,m,y,h,mn,s;
+            fatfs_convert_from_fat_time(dirent.write_time, &h,&m,&s);
+            fatfs_convert_from_fat_date(dirent.write_date, &d,&mn,&y);
+            FAT_PRINTF(("%02d/%02d/%04d  %02d:%02d      ", d,mn,y,h,m));
+#endif
+
+            if (dirent.is_dir)
+            {
+                FAT_PRINTF(("%s <DIR>\r\n", dirent.filename));
+            }
+            else
+            {
+                FAT_PRINTF(("%s [%d bytes]\r\n", dirent.filename, dirent.size));
+            }
+        }
+
+        fl_closedir(&dirstat);
+    }
+
+    FL_UNLOCK(&_fs);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_opendir: Opens a directory for listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+FL_DIR* fl_opendir(const char* path, FL_DIR *dir)
+{
+    int levels;
+    int res = 1;
+    uint32 cluster = FAT32_INVALID_CLUSTER;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+
+    levels = fatfs_total_path_levels((char*)path) + 1;
+
+    // If path is in the root dir
+    if (levels == 0)
+        cluster = fatfs_get_root_cluster(&_fs);
+    // Find parent directory start cluster
+    else
+        res = _open_directory((char*)path, &cluster);
+
+    if (res)
+        fatfs_list_directory_start(&_fs, dir, cluster);
+
+    FL_UNLOCK(&_fs);
+
+    return cluster != FAT32_INVALID_CLUSTER ? dir : 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_readdir: Get next item in directory
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_readdir(FL_DIR *dirls, fl_dirent *entry)
+{
+    int res = 0;
+
+    // If first call to library, initialise
+    CHECK_FL_INIT();
+
+    FL_LOCK(&_fs);
+
+    res = fatfs_list_directory_next(&_fs, dirls, entry);
+
+    FL_UNLOCK(&_fs);
+
+    return res ? 0 : -1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_closedir: Close directory after listing
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_closedir(FL_DIR* dir)
+{
+    // Not used
+    return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_is_dir: Is this a directory?
+//-----------------------------------------------------------------------------
+#if FATFS_DIR_LIST_SUPPORT
+int fl_is_dir(const char *path)
+{
+    int res = 0;
+    FL_DIR dir;
+
+    if (fl_opendir(path, &dir))
+    {
+        res = 1;
+        fl_closedir(&dir);
+    }
+
+    return res;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fl_format: Format a partition with either FAT16 or FAT32 based on size
+//-----------------------------------------------------------------------------
+#if FATFS_INC_FORMAT_SUPPORT
+int fl_format(uint32 volume_sectors, const char *name)
+{
+    return fatfs_format(&_fs, volume_sectors, name);
+}
+#endif /*FATFS_INC_FORMAT_SUPPORT*/
+//-----------------------------------------------------------------------------
+// fl_get_fs:
+//-----------------------------------------------------------------------------
+#ifdef FATFS_INC_TEST_HOOKS
+struct fatfs* fl_get_fs(void)
+{
+    return &_fs;
+}
+#endif
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_filelib.h b/vtoyjump/vtoyjump/fat_io_lib/fat_filelib.h
new file mode 100644 (file)
index 0000000..83cc877
--- /dev/null
@@ -0,0 +1,148 @@
+#ifndef __FAT_FILELIB_H__
+#define __FAT_FILELIB_H__
+
+#include "fat_opts.h"
+#include "fat_access.h"
+#include "fat_list.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#ifndef SEEK_CUR
+    #define SEEK_CUR    1
+#endif
+
+#ifndef SEEK_END
+    #define SEEK_END    2
+#endif
+
+#ifndef SEEK_SET
+    #define SEEK_SET    0
+#endif
+
+#ifndef EOF
+    #define EOF         (-1)
+#endif
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct sFL_FILE;
+
+struct cluster_lookup
+{
+    uint32 ClusterIdx;
+    uint32 CurrentCluster;
+};
+
+typedef struct sFL_FILE
+{
+    uint32                  parentcluster;
+    uint32                  startcluster;
+    uint32                  bytenum;
+    uint32                  filelength;
+    int                     filelength_changed;
+    char                    path[FATFS_MAX_LONG_FILENAME];
+    char                    filename[FATFS_MAX_LONG_FILENAME];
+    uint8                   shortfilename[11];
+
+#ifdef FAT_CLUSTER_CACHE_ENTRIES
+    uint32                  cluster_cache_idx[FAT_CLUSTER_CACHE_ENTRIES];
+    uint32                  cluster_cache_data[FAT_CLUSTER_CACHE_ENTRIES];
+#endif
+
+    // Cluster Lookup
+    struct cluster_lookup   last_fat_lookup;
+
+    // Read/Write sector buffer
+    uint8                   file_data_sector[FAT_SECTOR_SIZE];
+    uint32                  file_data_address;
+    int                     file_data_dirty;
+
+    // File fopen flags
+    uint8                   flags;
+#define FILE_READ           (1 << 0)
+#define FILE_WRITE          (1 << 1)
+#define FILE_APPEND         (1 << 2)
+#define FILE_BINARY         (1 << 3)
+#define FILE_ERASE          (1 << 4)
+#ifndef FILE_CREATE
+#define FILE_CREATE         (1 << 5)
+#endif
+
+    struct fat_node         list_node;
+} FL_FILE;
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+
+// External
+void                fl_init(void);
+void                fl_attach_locks(void (*lock)(void), void (*unlock)(void));
+int                 fl_attach_media(fn_diskio_read rd, fn_diskio_write wr);
+void                fl_shutdown(void);
+
+// Standard API
+void*               fl_fopen(const char *path, const char *modifiers);
+void                fl_fclose(void *file);
+int                 fl_fflush(void *file);
+int                 fl_fgetc(void *file);
+char *              fl_fgets(char *s, int n, void *f);
+int                 fl_fputc(int c, void *file);
+int                 fl_fputs(const char * str, void *file);
+int                 fl_fwrite(const void * data, int size, int count, void *file );
+int                 fl_fread(void * data, int size, int count, void *file );
+int                 fl_fseek(void *file , long offset , int origin );
+int                 fl_fgetpos(void *file , uint32 * position);
+long                fl_ftell(void *f);
+int                 fl_feof(void *f);
+int                 fl_remove(const char * filename);
+
+// Equivelant dirent.h
+typedef struct fs_dir_list_status    FL_DIR;
+typedef struct fs_dir_ent            fl_dirent;
+
+FL_DIR*             fl_opendir(const char* path, FL_DIR *dir);
+int                 fl_readdir(FL_DIR *dirls, fl_dirent *entry);
+int                 fl_closedir(FL_DIR* dir);
+
+// Extensions
+void                fl_listdirectory(const char *path);
+int                 fl_createdirectory(const char *path);
+int                 fl_is_dir(const char *path);
+
+int                 fl_format(uint32 volume_sectors, const char *name);
+
+// Test hooks
+#ifdef FATFS_INC_TEST_HOOKS
+struct fatfs*       fl_get_fs(void);
+#endif
+
+//-----------------------------------------------------------------------------
+// Stdio file I/O names
+//-----------------------------------------------------------------------------
+#ifdef USE_FILELIB_STDIO_COMPAT_NAMES
+
+#define FILE            FL_FILE
+
+#define fopen(a,b)      fl_fopen(a, b)
+#define fclose(a)       fl_fclose(a)
+#define fflush(a)       fl_fflush(a)
+#define fgetc(a)        fl_fgetc(a)
+#define fgets(a,b,c)    fl_fgets(a, b, c)
+#define fputc(a,b)      fl_fputc(a, b)
+#define fputs(a,b)      fl_fputs(a, b)
+#define fwrite(a,b,c,d) fl_fwrite(a, b, c, d)
+#define fread(a,b,c,d)  fl_fread(a, b, c, d)
+#define fseek(a,b,c)    fl_fseek(a, b, c)
+#define fgetpos(a,b)    fl_fgetpos(a, b)
+#define ftell(a)        fl_ftell(a)
+#define feof(a)         fl_feof(a)
+#define remove(a)       fl_remove(a)
+#define mkdir(a)        fl_createdirectory(a)
+#define rmdir(a)        0
+
+#endif
+
+#endif
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_format.c b/vtoyjump/vtoyjump/fat_io_lib/fat_format.c
new file mode 100644 (file)
index 0000000..fee8056
--- /dev/null
@@ -0,0 +1,532 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//                            FAT16/32 File IO Library
+//                                    V2.6
+//                              Ultra-Embedded.com
+//                            Copyright 2003 - 2012
+//
+//                         Email: admin@ultra-embedded.com
+//
+//                                License: GPL
+//   If you would like a version with a more permissive license for use in
+//   closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+#include "fat_format.h"
+
+#if FATFS_INC_FORMAT_SUPPORT
+
+//-----------------------------------------------------------------------------
+// Tables
+//-----------------------------------------------------------------------------
+struct sec_per_clus_table
+{
+    uint32  sectors;
+    uint8   sectors_per_cluster;
+};
+
+struct sec_per_clus_table _cluster_size_table16[] =
+{
+    { 32680, 2},    // 16MB - 1K
+    { 262144, 4},   // 128MB - 2K
+    { 524288, 8},   // 256MB - 4K
+    { 1048576, 16}, // 512MB - 8K
+    { 2097152, 32}, // 1GB - 16K
+    { 4194304, 64}, // 2GB - 32K
+    { 8388608, 128},// 2GB - 64K [Warning only supported by Windows XP onwards]
+    { 0 , 0 }       // Invalid
+};
+
+struct sec_per_clus_table _cluster_size_table32[] =
+{
+    { 532480, 1},     // 260MB - 512b
+    { 16777216, 8},   // 8GB - 4K
+    { 33554432, 16},  // 16GB - 8K
+    { 67108864, 32},  // 32GB - 16K
+    { 0xFFFFFFFF, 64},// >32GB - 32K
+    { 0 , 0 }         // Invalid
+};
+
+//-----------------------------------------------------------------------------
+// fatfs_calc_cluster_size: Calculate what cluster size should be used
+//-----------------------------------------------------------------------------
+static uint8 fatfs_calc_cluster_size(uint32 sectors, int is_fat32)
+{
+    int i;
+
+    if (!is_fat32)
+    {
+        for (i=0; _cluster_size_table16[i].sectors_per_cluster != 0;i++)
+            if (sectors <= _cluster_size_table16[i].sectors)
+                return _cluster_size_table16[i].sectors_per_cluster;
+    }
+    else
+    {
+        for (i=0; _cluster_size_table32[i].sectors_per_cluster != 0;i++)
+            if (sectors <= _cluster_size_table32[i].sectors)
+                return _cluster_size_table32[i].sectors_per_cluster;
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_erase_sectors: Erase a number of sectors
+//-----------------------------------------------------------------------------
+static int fatfs_erase_sectors(struct fatfs *fs, uint32 lba, int count)
+{
+    int i;
+
+    // Zero sector first
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+    for (i=0;i<count;i++)
+        if (!fs->disk_io.write_media(lba + i, fs->currentsector.sector, 1))
+            return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_boot_sector: Create the boot sector
+//-----------------------------------------------------------------------------
+static int fatfs_create_boot_sector(struct fatfs *fs, uint32 boot_sector_lba, uint32 vol_sectors, const char *name, int is_fat32)
+{
+    uint32 total_clusters;
+    int i;
+
+    // Zero sector initially
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+    // OEM Name & Jump Code
+    fs->currentsector.sector[0] = 0xEB;
+    fs->currentsector.sector[1] = 0x3C;
+    fs->currentsector.sector[2] = 0x90;
+    fs->currentsector.sector[3] = 0x4D;
+    fs->currentsector.sector[4] = 0x53;
+    fs->currentsector.sector[5] = 0x44;
+    fs->currentsector.sector[6] = 0x4F;
+    fs->currentsector.sector[7] = 0x53;
+    fs->currentsector.sector[8] = 0x35;
+    fs->currentsector.sector[9] = 0x2E;
+    fs->currentsector.sector[10] = 0x30;
+
+    // Bytes per sector
+    fs->currentsector.sector[11] = (FAT_SECTOR_SIZE >> 0) & 0xFF;
+    fs->currentsector.sector[12] = (FAT_SECTOR_SIZE >> 8) & 0xFF;
+
+    // Get sectors per cluster size for the disk
+    fs->sectors_per_cluster = fatfs_calc_cluster_size(vol_sectors, is_fat32);
+    if (!fs->sectors_per_cluster)
+        return 0; // Invalid disk size
+
+    // Sectors per cluster
+    fs->currentsector.sector[13] = fs->sectors_per_cluster;
+
+    // Reserved Sectors
+    if (!is_fat32)
+        fs->reserved_sectors = 8;
+    else
+        fs->reserved_sectors = 32;
+    fs->currentsector.sector[14] = (fs->reserved_sectors >> 0) & 0xFF;
+    fs->currentsector.sector[15] = (fs->reserved_sectors >> 8) & 0xFF;
+
+    // Number of FATS
+    fs->num_of_fats = 2;
+    fs->currentsector.sector[16] = fs->num_of_fats;
+
+    // Max entries in root dir (FAT16 only)
+    if (!is_fat32)
+    {
+        fs->root_entry_count = 512;
+        fs->currentsector.sector[17] = (fs->root_entry_count >> 0) & 0xFF;
+        fs->currentsector.sector[18] = (fs->root_entry_count >> 8) & 0xFF;
+    }
+    else
+    {
+        fs->root_entry_count = 0;
+        fs->currentsector.sector[17] = 0;
+        fs->currentsector.sector[18] = 0;
+    }
+
+    // [FAT16] Total sectors (use FAT32 count instead)
+    fs->currentsector.sector[19] = 0x00;
+    fs->currentsector.sector[20] = 0x00;
+
+    // Media type
+    fs->currentsector.sector[21] = 0xF8;
+
+
+    // FAT16 BS Details
+    if (!is_fat32)
+    {
+        // Count of sectors used by the FAT table (FAT16 only)
+        total_clusters = (vol_sectors / fs->sectors_per_cluster) + 1;
+        fs->fat_sectors = (total_clusters/(FAT_SECTOR_SIZE/2)) + 1;
+        fs->currentsector.sector[22] = (uint8)((fs->fat_sectors >> 0) & 0xFF);
+        fs->currentsector.sector[23] = (uint8)((fs->fat_sectors >> 8) & 0xFF);
+
+        // Sectors per track
+        fs->currentsector.sector[24] = 0x00;
+        fs->currentsector.sector[25] = 0x00;
+
+        // Heads
+        fs->currentsector.sector[26] = 0x00;
+        fs->currentsector.sector[27] = 0x00;
+
+        // Hidden sectors
+        fs->currentsector.sector[28] = 0x20;
+        fs->currentsector.sector[29] = 0x00;
+        fs->currentsector.sector[30] = 0x00;
+        fs->currentsector.sector[31] = 0x00;
+
+        // Total sectors for this volume
+        fs->currentsector.sector[32] = (uint8)((vol_sectors>>0)&0xFF);
+        fs->currentsector.sector[33] = (uint8)((vol_sectors>>8)&0xFF);
+        fs->currentsector.sector[34] = (uint8)((vol_sectors>>16)&0xFF);
+        fs->currentsector.sector[35] = (uint8)((vol_sectors>>24)&0xFF);
+
+        // Drive number
+        fs->currentsector.sector[36] = 0x00;
+
+        // Reserved
+        fs->currentsector.sector[37] = 0x00;
+
+        // Boot signature
+        fs->currentsector.sector[38] = 0x29;
+
+        // Volume ID
+        fs->currentsector.sector[39] = 0x12;
+        fs->currentsector.sector[40] = 0x34;
+        fs->currentsector.sector[41] = 0x56;
+        fs->currentsector.sector[42] = 0x78;
+
+        // Volume name
+        for (i=0;i<11;i++)
+        {
+            if (i < (int)strlen(name))
+                fs->currentsector.sector[i+43] = name[i];
+            else
+                fs->currentsector.sector[i+43] = ' ';
+        }
+
+        // File sys type
+        fs->currentsector.sector[54] = 'F';
+        fs->currentsector.sector[55] = 'A';
+        fs->currentsector.sector[56] = 'T';
+        fs->currentsector.sector[57] = '1';
+        fs->currentsector.sector[58] = '6';
+        fs->currentsector.sector[59] = ' ';
+        fs->currentsector.sector[60] = ' ';
+        fs->currentsector.sector[61] = ' ';
+
+        // Signature
+        fs->currentsector.sector[510] = 0x55;
+        fs->currentsector.sector[511] = 0xAA;
+    }
+    // FAT32 BS Details
+    else
+    {
+        // Count of sectors used by the FAT table (FAT16 only)
+        fs->currentsector.sector[22] = 0;
+        fs->currentsector.sector[23] = 0;
+
+        // Sectors per track (default)
+        fs->currentsector.sector[24] = 0x3F;
+        fs->currentsector.sector[25] = 0x00;
+
+        // Heads (default)
+        fs->currentsector.sector[26] = 0xFF;
+        fs->currentsector.sector[27] = 0x00;
+
+        // Hidden sectors
+        fs->currentsector.sector[28] = 0x00;
+        fs->currentsector.sector[29] = 0x00;
+        fs->currentsector.sector[30] = 0x00;
+        fs->currentsector.sector[31] = 0x00;
+
+        // Total sectors for this volume
+        fs->currentsector.sector[32] = (uint8)((vol_sectors>>0)&0xFF);
+        fs->currentsector.sector[33] = (uint8)((vol_sectors>>8)&0xFF);
+        fs->currentsector.sector[34] = (uint8)((vol_sectors>>16)&0xFF);
+        fs->currentsector.sector[35] = (uint8)((vol_sectors>>24)&0xFF);
+
+        total_clusters = (vol_sectors / fs->sectors_per_cluster) + 1;
+        fs->fat_sectors = (total_clusters/(FAT_SECTOR_SIZE/4)) + 1;
+
+        // BPB_FATSz32
+        fs->currentsector.sector[36] = (uint8)((fs->fat_sectors>>0)&0xFF);
+        fs->currentsector.sector[37] = (uint8)((fs->fat_sectors>>8)&0xFF);
+        fs->currentsector.sector[38] = (uint8)((fs->fat_sectors>>16)&0xFF);
+        fs->currentsector.sector[39] = (uint8)((fs->fat_sectors>>24)&0xFF);
+
+        // BPB_ExtFlags
+        fs->currentsector.sector[40] = 0;
+        fs->currentsector.sector[41] = 0;
+
+        // BPB_FSVer
+        fs->currentsector.sector[42] = 0;
+        fs->currentsector.sector[43] = 0;
+
+        // BPB_RootClus
+        fs->currentsector.sector[44] = (uint8)((fs->rootdir_first_cluster>>0)&0xFF);
+        fs->currentsector.sector[45] = (uint8)((fs->rootdir_first_cluster>>8)&0xFF);
+        fs->currentsector.sector[46] = (uint8)((fs->rootdir_first_cluster>>16)&0xFF);
+        fs->currentsector.sector[47] = (uint8)((fs->rootdir_first_cluster>>24)&0xFF);
+
+        // BPB_FSInfo
+        fs->currentsector.sector[48] = (uint8)((fs->fs_info_sector>>0)&0xFF);
+        fs->currentsector.sector[49] = (uint8)((fs->fs_info_sector>>8)&0xFF);
+
+        // BPB_BkBootSec
+        fs->currentsector.sector[50] = 6;
+        fs->currentsector.sector[51] = 0;
+
+        // Drive number
+        fs->currentsector.sector[64] = 0x00;
+
+        // Boot signature
+        fs->currentsector.sector[66] = 0x29;
+
+        // Volume ID
+        fs->currentsector.sector[67] = 0x12;
+        fs->currentsector.sector[68] = 0x34;
+        fs->currentsector.sector[69] = 0x56;
+        fs->currentsector.sector[70] = 0x78;
+
+        // Volume name
+        for (i=0;i<11;i++)
+        {
+            if (i < (int)strlen(name))
+                fs->currentsector.sector[i+71] = name[i];
+            else
+                fs->currentsector.sector[i+71] = ' ';
+        }
+
+        // File sys type
+        fs->currentsector.sector[82] = 'F';
+        fs->currentsector.sector[83] = 'A';
+        fs->currentsector.sector[84] = 'T';
+        fs->currentsector.sector[85] = '3';
+        fs->currentsector.sector[86] = '2';
+        fs->currentsector.sector[87] = ' ';
+        fs->currentsector.sector[88] = ' ';
+        fs->currentsector.sector[89] = ' ';
+
+        // Signature
+        fs->currentsector.sector[510] = 0x55;
+        fs->currentsector.sector[511] = 0xAA;
+    }
+
+    if (fs->disk_io.write_media(boot_sector_lba, fs->currentsector.sector, 1))
+        return 1;
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_fsinfo_sector: Create the FSInfo sector (FAT32)
+//-----------------------------------------------------------------------------
+static int fatfs_create_fsinfo_sector(struct fatfs *fs, uint32 sector_lba)
+{
+    // Zero sector initially
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+    // FSI_LeadSig
+    fs->currentsector.sector[0] = 0x52;
+    fs->currentsector.sector[1] = 0x52;
+    fs->currentsector.sector[2] = 0x61;
+    fs->currentsector.sector[3] = 0x41;
+
+    // FSI_StrucSig
+    fs->currentsector.sector[484] = 0x72;
+    fs->currentsector.sector[485] = 0x72;
+    fs->currentsector.sector[486] = 0x41;
+    fs->currentsector.sector[487] = 0x61;
+
+    // FSI_Free_Count
+    fs->currentsector.sector[488] = 0xFF;
+    fs->currentsector.sector[489] = 0xFF;
+    fs->currentsector.sector[490] = 0xFF;
+    fs->currentsector.sector[491] = 0xFF;
+
+    // FSI_Nxt_Free
+    fs->currentsector.sector[492] = 0xFF;
+    fs->currentsector.sector[493] = 0xFF;
+    fs->currentsector.sector[494] = 0xFF;
+    fs->currentsector.sector[495] = 0xFF;
+
+    // Signature
+    fs->currentsector.sector[510] = 0x55;
+    fs->currentsector.sector[511] = 0xAA;
+
+    if (fs->disk_io.write_media(sector_lba, fs->currentsector.sector, 1))
+        return 1;
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_erase_fat: Erase FAT table using fs details in fs struct
+//-----------------------------------------------------------------------------
+static int fatfs_erase_fat(struct fatfs *fs, int is_fat32)
+{
+    uint32 i;
+
+    // Zero sector initially
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+
+    // Initialise default allocate / reserved clusters
+    if (!is_fat32)
+    {
+        SET_16BIT_WORD(fs->currentsector.sector, 0, 0xFFF8);
+        SET_16BIT_WORD(fs->currentsector.sector, 2, 0xFFFF);
+    }
+    else
+    {
+        SET_32BIT_WORD(fs->currentsector.sector, 0, 0x0FFFFFF8);
+        SET_32BIT_WORD(fs->currentsector.sector, 4, 0xFFFFFFFF);
+        SET_32BIT_WORD(fs->currentsector.sector, 8, 0x0FFFFFFF);
+    }
+
+    if (!fs->disk_io.write_media(fs->fat_begin_lba + 0, fs->currentsector.sector, 1))
+        return 0;
+
+    // Zero remaining FAT sectors
+    memset(fs->currentsector.sector, 0, FAT_SECTOR_SIZE);
+    for (i=1;i<fs->fat_sectors*fs->num_of_fats;i++)
+        if (!fs->disk_io.write_media(fs->fat_begin_lba + i, fs->currentsector.sector, 1))
+            return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format_fat16: Format a FAT16 partition
+//-----------------------------------------------------------------------------
+int fatfs_format_fat16(struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+    fs->currentsector.address = FAT32_INVALID_CLUSTER;
+    fs->currentsector.dirty = 0;
+
+    fs->next_free_cluster = 0; // Invalid
+
+    fatfs_fat_init(fs);
+
+    // Make sure we have read + write functions
+    if (!fs->disk_io.read_media || !fs->disk_io.write_media)
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // Volume is FAT16
+    fs->fat_type = FAT_TYPE_16;
+
+    // Not valid for FAT16
+    fs->fs_info_sector = 0;
+    fs->rootdir_first_cluster = 0;
+
+    // Sector 0: Boot sector
+    // NOTE: We don't need an MBR, it is a waste of a good sector!
+    fs->lba_begin = 0;
+    if (!fatfs_create_boot_sector(fs, fs->lba_begin, volume_sectors, name, 0))
+        return 0;
+
+    // For FAT16 (which this may be), rootdir_first_cluster is actuall rootdir_first_sector
+    fs->rootdir_first_sector = fs->reserved_sectors + (fs->num_of_fats * fs->fat_sectors);
+    fs->rootdir_sectors = ((fs->root_entry_count * 32) + (FAT_SECTOR_SIZE - 1)) / FAT_SECTOR_SIZE;
+
+    // First FAT LBA address
+    fs->fat_begin_lba = fs->lba_begin + fs->reserved_sectors;
+
+    // The address of the first data cluster on this volume
+    fs->cluster_begin_lba = fs->fat_begin_lba + (fs->num_of_fats * fs->fat_sectors);
+
+    // Initialise FAT sectors
+    if (!fatfs_erase_fat(fs, 0))
+        return 0;
+
+    // Erase Root directory
+    if (!fatfs_erase_sectors(fs, fs->lba_begin + fs->rootdir_first_sector, fs->rootdir_sectors))
+        return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format_fat32: Format a FAT32 partition
+//-----------------------------------------------------------------------------
+int fatfs_format_fat32(struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+    fs->currentsector.address = FAT32_INVALID_CLUSTER;
+    fs->currentsector.dirty = 0;
+
+    fs->next_free_cluster = 0; // Invalid
+
+    fatfs_fat_init(fs);
+
+    // Make sure we have read + write functions
+    if (!fs->disk_io.read_media || !fs->disk_io.write_media)
+        return FAT_INIT_MEDIA_ACCESS_ERROR;
+
+    // Volume is FAT32
+    fs->fat_type = FAT_TYPE_32;
+
+    // Basic defaults for normal FAT32 partitions
+    fs->fs_info_sector = 1;
+    fs->rootdir_first_cluster = 2;
+
+    // Sector 0: Boot sector
+    // NOTE: We don't need an MBR, it is a waste of a good sector!
+    fs->lba_begin = 0;
+    if (!fatfs_create_boot_sector(fs, fs->lba_begin, volume_sectors, name, 1))
+        return 0;
+
+    // First FAT LBA address
+    fs->fat_begin_lba = fs->lba_begin + fs->reserved_sectors;
+
+    // The address of the first data cluster on this volume
+    fs->cluster_begin_lba = fs->fat_begin_lba + (fs->num_of_fats * fs->fat_sectors);
+
+    // Initialise FSInfo sector
+    if (!fatfs_create_fsinfo_sector(fs, fs->fs_info_sector))
+        return 0;
+
+    // Initialise FAT sectors
+    if (!fatfs_erase_fat(fs, 1))
+        return 0;
+
+    // Erase Root directory
+    if (!fatfs_erase_sectors(fs, fatfs_lba_of_cluster(fs, fs->rootdir_first_cluster), fs->sectors_per_cluster))
+        return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_format: Format a partition with either FAT16 or FAT32 based on size
+//-----------------------------------------------------------------------------
+int fatfs_format(struct fatfs *fs, uint32 volume_sectors, const char *name)
+{
+    // 2GB - 32K limit for safe behaviour for FAT16
+    if (volume_sectors <= 4194304)
+        return fatfs_format_fat16(fs, volume_sectors, name);
+    else
+        return fatfs_format_fat32(fs, volume_sectors, name);
+}
+#endif /*FATFS_INC_FORMAT_SUPPORT*/
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_format.h b/vtoyjump/vtoyjump/fat_io_lib/fat_format.h
new file mode 100644 (file)
index 0000000..a8a6bba
--- /dev/null
@@ -0,0 +1,15 @@
+#ifndef __FAT_FORMAT_H__
+#define __FAT_FORMAT_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+#include "fat_access.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_format(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat16(struct fatfs *fs, uint32 volume_sectors, const char *name);
+int fatfs_format_fat32(struct fatfs *fs, uint32 volume_sectors, const char *name);
+
+#endif
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_list.h b/vtoyjump/vtoyjump/fat_io_lib/fat_list.h
new file mode 100644 (file)
index 0000000..bd386ef
--- /dev/null
@@ -0,0 +1,161 @@
+#ifndef __FAT_LIST_H__
+#define __FAT_LIST_H__
+
+#ifndef FAT_ASSERT
+    #define FAT_ASSERT(x)
+#endif
+
+#ifndef FAT_INLINE
+    #define FAT_INLINE
+#endif
+
+//-----------------------------------------------------------------
+// Types
+//-----------------------------------------------------------------
+struct fat_list;
+
+struct fat_node
+{
+    struct fat_node    *previous;
+    struct fat_node    *next;
+};
+
+struct fat_list
+{
+    struct fat_node    *head;
+    struct fat_node    *tail;
+};
+
+//-----------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------
+#define fat_list_entry(p, t, m)     p ? ((t *)((char *)(p)-(char*)(&((t *)0)->m))) : 0
+#define fat_list_next(l, p)         (p)->next
+#define fat_list_prev(l, p)         (p)->previous
+#define fat_list_first(l)           (l)->head
+#define fat_list_last(l)            (l)->tail
+#define fat_list_for_each(l, p)     for ((p) = (l)->head; (p); (p) = (p)->next)
+
+//-----------------------------------------------------------------
+// Inline Functions
+//-----------------------------------------------------------------
+
+//-----------------------------------------------------------------
+// fat_list_init:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_init(struct fat_list *list)
+{
+    FAT_ASSERT(list);
+
+    list->head = list->tail = 0;
+}
+//-----------------------------------------------------------------
+// fat_list_remove:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_remove(struct fat_list *list, struct fat_node *node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+
+    if(!node->previous)
+        list->head = node->next;
+    else
+        node->previous->next = node->next;
+
+    if(!node->next)
+        list->tail = node->previous;
+    else
+        node->next->previous = node->previous;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_after:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_after(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+    FAT_ASSERT(new_node);
+
+    new_node->previous = node;
+    new_node->next = node->next;
+    if (!node->next)
+        list->tail = new_node;
+    else
+        node->next->previous = new_node;
+    node->next = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_before:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_before(struct fat_list *list, struct fat_node *node, struct fat_node *new_node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+    FAT_ASSERT(new_node);
+
+    new_node->previous = node->previous;
+    new_node->next = node;
+    if (!node->previous)
+        list->head = new_node;
+    else
+        node->previous->next = new_node;
+    node->previous = new_node;
+}
+//-----------------------------------------------------------------
+// fat_list_insert_first:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_first(struct fat_list *list, struct fat_node *node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+
+    if (!list->head)
+    {
+        list->head = node;
+        list->tail = node;
+        node->previous = 0;
+        node->next = 0;
+    }
+    else
+        fat_list_insert_before(list, list->head, node);
+}
+//-----------------------------------------------------------------
+// fat_list_insert_last:
+//-----------------------------------------------------------------
+static FAT_INLINE void fat_list_insert_last(struct fat_list *list, struct fat_node *node)
+{
+    FAT_ASSERT(list);
+    FAT_ASSERT(node);
+
+    if (!list->tail)
+        fat_list_insert_first(list, node);
+     else
+        fat_list_insert_after(list, list->tail, node);
+}
+//-----------------------------------------------------------------
+// fat_list_is_empty:
+//-----------------------------------------------------------------
+static FAT_INLINE int fat_list_is_empty(struct fat_list *list)
+{
+    FAT_ASSERT(list);
+
+    return !list->head;
+}
+//-----------------------------------------------------------------
+// fat_list_pop_head:
+//-----------------------------------------------------------------
+static FAT_INLINE struct fat_node * fat_list_pop_head(struct fat_list *list)
+{
+    struct fat_node * node;
+
+    FAT_ASSERT(list);
+
+    node = fat_list_first(list);
+    if (node)
+        fat_list_remove(list, node);
+
+    return node;
+}
+
+#endif
+
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_misc.c b/vtoyjump/vtoyjump/fat_io_lib/fat_misc.c
new file mode 100644 (file)
index 0000000..4c7dfab
--- /dev/null
@@ -0,0 +1,505 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//                            FAT16/32 File IO Library
+//                                    V2.6
+//                              Ultra-Embedded.com
+//                            Copyright 2003 - 2012
+//
+//                         Email: admin@ultra-embedded.com
+//
+//                                License: GPL
+//   If you would like a version with a more permissive license for use in
+//   closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include <stdlib.h>
+#include <string.h>
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_init: Clear long file name cache
+//-----------------------------------------------------------------------------
+void fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable)
+{
+    int i = 0;
+
+    lfn->no_of_strings = 0;
+
+#if FATFS_INC_LFN_SUPPORT
+
+    // Zero out buffer also
+    if (wipeTable)
+        for (i=0;i<MAX_LONGFILENAME_ENTRIES;i++)
+            memset(lfn->String[i], 0x00, MAX_LFN_ENTRY_LENGTH);
+#endif
+}
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_entry - Function extracts long file name text from sector
+// at a specific offset
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+void fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer)
+{
+    uint8 LFNIndex, i;
+    LFNIndex = entryBuffer[0] & 0x1F;
+
+    // Limit file name to cache size!
+    if (LFNIndex > MAX_LONGFILENAME_ENTRIES)
+        return ;
+
+    // This is an error condition
+    if (LFNIndex == 0)
+        return ;
+
+    if (lfn->no_of_strings == 0)
+        lfn->no_of_strings = LFNIndex;
+
+    lfn->String[LFNIndex-1][0] = entryBuffer[1];
+    lfn->String[LFNIndex-1][1] = entryBuffer[3];
+    lfn->String[LFNIndex-1][2] = entryBuffer[5];
+    lfn->String[LFNIndex-1][3] = entryBuffer[7];
+    lfn->String[LFNIndex-1][4] = entryBuffer[9];
+    lfn->String[LFNIndex-1][5] = entryBuffer[0x0E];
+    lfn->String[LFNIndex-1][6] = entryBuffer[0x10];
+    lfn->String[LFNIndex-1][7] = entryBuffer[0x12];
+    lfn->String[LFNIndex-1][8] = entryBuffer[0x14];
+    lfn->String[LFNIndex-1][9] = entryBuffer[0x16];
+    lfn->String[LFNIndex-1][10] = entryBuffer[0x18];
+    lfn->String[LFNIndex-1][11] = entryBuffer[0x1C];
+    lfn->String[LFNIndex-1][12] = entryBuffer[0x1E];
+
+    for (i=0; i<MAX_LFN_ENTRY_LENGTH; i++)
+        if (lfn->String[LFNIndex-1][i]==0xFF)
+            lfn->String[LFNIndex-1][i] = 0x20; // Replace with spaces
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_cache_get: Get a reference to the long filename
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+char* fatfs_lfn_cache_get(struct lfn_cache *lfn)
+{
+    // Null terminate long filename
+    if (lfn->no_of_strings == MAX_LONGFILENAME_ENTRIES)
+        lfn->Null = '\0';
+    else if (lfn->no_of_strings)
+        lfn->String[lfn->no_of_strings][0] = '\0';
+    else
+        lfn->String[0][0] = '\0';
+
+    return (char*)&lfn->String[0][0];
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_text: If LFN text entry found
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_text(struct fat_dir_entry *entry)
+{
+    if ((entry->Attr & FILE_ATTR_LFN_TEXT) == FILE_ATTR_LFN_TEXT)
+        return 1;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_invalid: If SFN found not relating to LFN
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_invalid(struct fat_dir_entry *entry)
+{
+    if ( (entry->Name[0]==FILE_HEADER_BLANK)  ||
+         (entry->Name[0]==FILE_HEADER_DELETED)||
+         (entry->Attr==FILE_ATTR_VOLUME_ID) ||
+         (entry->Attr & FILE_ATTR_SYSHID) )
+        return 1;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_lfn_exists: If LFN exists and correlation SFN found
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry)
+{
+    if ( (entry->Attr!=FILE_ATTR_LFN_TEXT) &&
+         (entry->Name[0]!=FILE_HEADER_BLANK) &&
+         (entry->Name[0]!=FILE_HEADER_DELETED) &&
+         (entry->Attr!=FILE_ATTR_VOLUME_ID) &&
+         (!(entry->Attr&FILE_ATTR_SYSHID)) &&
+         (lfn->no_of_strings) )
+        return 1;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_entry_sfn_only: If SFN only exists
+//-----------------------------------------------------------------------------
+int fatfs_entry_sfn_only(struct fat_dir_entry *entry)
+{
+    if ( (entry->Attr!=FILE_ATTR_LFN_TEXT) &&
+         (entry->Name[0]!=FILE_HEADER_BLANK) &&
+         (entry->Name[0]!=FILE_HEADER_DELETED) &&
+         (entry->Attr!=FILE_ATTR_VOLUME_ID) &&
+         (!(entry->Attr&FILE_ATTR_SYSHID)) )
+        return 1;
+    else
+        return 0;
+}
+// TODO: FILE_ATTR_SYSHID ?!?!??!
+//-----------------------------------------------------------------------------
+// fatfs_entry_is_dir: Returns 1 if a directory
+//-----------------------------------------------------------------------------
+int fatfs_entry_is_dir(struct fat_dir_entry *entry)
+{
+    if (entry->Attr & FILE_TYPE_DIR)
+        return 1;
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_entry_is_file: Returns 1 is a file entry
+//-----------------------------------------------------------------------------
+int fatfs_entry_is_file(struct fat_dir_entry *entry)
+{
+    if (entry->Attr & FILE_TYPE_FILE)
+        return 1;
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_lfn_entries_required: Calculate number of 13 characters entries
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+int fatfs_lfn_entries_required(char *filename)
+{
+    int length = (int)strlen(filename);
+
+    if (length)
+        return (length + MAX_LFN_ENTRY_LENGTH - 1) / MAX_LFN_ENTRY_LENGTH;
+    else
+        return 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_filename_to_lfn:
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+void fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk)
+{
+    int i;
+    int nameIndexes[MAX_LFN_ENTRY_LENGTH] = {1,3,5,7,9,0x0E,0x10,0x12,0x14,0x16,0x18,0x1C,0x1E};
+
+    // 13 characters entries
+    int length = (int)strlen(filename);
+    int entriesRequired = fatfs_lfn_entries_required(filename);
+
+    // Filename offset
+    int start = entry * MAX_LFN_ENTRY_LENGTH;
+
+    // Initialise to zeros
+    memset(buffer, 0x00, FAT_DIR_ENTRY_SIZE);
+
+    // LFN entry number
+    buffer[0] = (uint8)(((entriesRequired-1)==entry)?(0x40|(entry+1)):(entry+1));
+
+    // LFN flag
+    buffer[11] = 0x0F;
+
+    // Checksum of short filename
+    buffer[13] = sfnChk;
+
+    // Copy to buffer
+    for (i=0;i<MAX_LFN_ENTRY_LENGTH;i++)
+    {
+        if ( (start+i) < length )
+            buffer[nameIndexes[i]] = filename[start+i];
+        else if ( (start+i) == length )
+            buffer[nameIndexes[i]] = 0x00;
+        else
+        {
+            buffer[nameIndexes[i]] = 0xFF;
+            buffer[nameIndexes[i]+1] = 0xFF;
+        }
+    }
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_sfn_create_entry: Create the short filename directory entry
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+void fatfs_sfn_create_entry(char *shortfilename, uint32 size, uint32 startCluster, struct fat_dir_entry *entry, int dir)
+{
+    int i;
+
+    // Copy short filename
+    for (i=0;i<FAT_SFN_SIZE_FULL;i++)
+        entry->Name[i] = shortfilename[i];
+
+    // Unless we have a RTC we might as well set these to 1980
+    entry->CrtTimeTenth = 0x00;
+    entry->CrtTime[1] = entry->CrtTime[0] = 0x00;
+    entry->CrtDate[1] = 0x00;
+    entry->CrtDate[0] = 0x20;
+    entry->LstAccDate[1] = 0x00;
+    entry->LstAccDate[0] = 0x20;
+    entry->WrtTime[1] = entry->WrtTime[0] = 0x00;
+    entry->WrtDate[1] = 0x00;
+    entry->WrtDate[0] = 0x20;
+
+    if (!dir)
+        entry->Attr = FILE_TYPE_FILE;
+    else
+        entry->Attr = FILE_TYPE_DIR;
+
+    entry->NTRes = 0x00;
+
+    entry->FstClusHI = FAT_HTONS((uint16)((startCluster>>16) & 0xFFFF));
+    entry->FstClusLO = FAT_HTONS((uint16)((startCluster>>0) & 0xFFFF));
+    entry->FileSize = FAT_HTONL(size);
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_create_sfn: Create a padded SFN
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_lfn_create_sfn(char *sfn_output, char *filename)
+{
+    int i;
+    int dotPos = -1;
+    char ext[3];
+    int pos;
+    int len = (int)strlen(filename);
+
+    // Invalid to start with .
+    if (filename[0]=='.')
+        return 0;
+
+    memset(sfn_output, ' ', FAT_SFN_SIZE_FULL);
+    memset(ext, ' ', 3);
+
+    // Find dot seperator
+    for (i = 0; i< len; i++)
+    {
+        if (filename[i]=='.')
+            dotPos = i;
+    }
+
+    // Extract extensions
+    if (dotPos!=-1)
+    {
+        // Copy first three chars of extension
+        for (i = (dotPos+1); i < (dotPos+1+3); i++)
+            if (i<len)
+                ext[i-(dotPos+1)] = filename[i];
+
+        // Shorten the length to the dot position
+        len = dotPos;
+    }
+
+    // Add filename part
+    pos = 0;
+    for (i=0;i<len;i++)
+    {
+        if ( (filename[i]!=' ') && (filename[i]!='.') )
+        {
+            if (filename[i] >= 'a' && filename[i] <= 'z')
+                sfn_output[pos++] = filename[i] - 'a' + 'A';
+            else
+                sfn_output[pos++] = filename[i];
+        }
+
+        // Fill upto 8 characters
+        if (pos==FAT_SFN_SIZE_PARTIAL)
+            break;
+    }
+
+    // Add extension part
+    for (i=FAT_SFN_SIZE_PARTIAL;i<FAT_SFN_SIZE_FULL;i++)
+    {
+        if (ext[i-FAT_SFN_SIZE_PARTIAL] >= 'a' && ext[i-FAT_SFN_SIZE_PARTIAL] <= 'z')
+            sfn_output[i] = ext[i-FAT_SFN_SIZE_PARTIAL] - 'a' + 'A';
+        else
+            sfn_output[i] = ext[i-FAT_SFN_SIZE_PARTIAL];
+    }
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_itoa:
+//-----------------------------------------------------------------------------
+static void fatfs_itoa(uint32 num, char *s)
+{
+    char* cp;
+    char outbuf[12];
+    const char digits[] = "0123456789ABCDEF";
+
+    // Build string backwards
+    cp = outbuf;
+    do
+    {
+        *cp++ = digits[(int)(num % 10)];
+    }
+    while ((num /= 10) > 0);
+
+    *cp-- = 0;
+
+    // Copy in forwards
+    while (cp >= outbuf)
+        *s++ = *cp--;
+
+    *s = 0;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_lfn_generate_tail:
+// sfn_input = Input short filename, spaced format & in upper case
+// sfn_output = Output short filename with tail
+//-----------------------------------------------------------------------------
+#if FATFS_INC_LFN_SUPPORT
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum)
+{
+    int tail_chars;
+    char tail_str[12];
+
+    if (tailNum > 99999)
+        return 0;
+
+    // Convert to number
+    memset(tail_str, 0x00, sizeof(tail_str));
+    tail_str[0] = '~';
+    fatfs_itoa(tailNum, tail_str+1);
+
+    // Copy in base filename
+    memcpy(sfn_output, sfn_input, FAT_SFN_SIZE_FULL);
+
+    // Overwrite with tail
+    tail_chars = (int)strlen(tail_str);
+    memcpy(sfn_output+(FAT_SFN_SIZE_PARTIAL-tail_chars), tail_str, tail_chars);
+
+    return 1;
+}
+#endif
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_convert_from_fat_time: Convert FAT time to h/m/s
+//-----------------------------------------------------------------------------
+#if FATFS_INC_TIME_DATE_SUPPORT
+void fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds)
+{
+    *hours = (fat_time >> FAT_TIME_HOURS_SHIFT) & FAT_TIME_HOURS_MASK;
+    *minutes = (fat_time >> FAT_TIME_MINUTES_SHIFT) & FAT_TIME_MINUTES_MASK;
+    *seconds = (fat_time >> FAT_TIME_SECONDS_SHIFT) & FAT_TIME_SECONDS_MASK;
+    *seconds = *seconds * FAT_TIME_SECONDS_SCALE;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_from_fat_date: Convert FAT date to d/m/y
+//-----------------------------------------------------------------------------
+void fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year)
+{
+    *day = (fat_date >> FAT_DATE_DAY_SHIFT) & FAT_DATE_DAY_MASK;
+    *month = (fat_date >> FAT_DATE_MONTH_SHIFT) & FAT_DATE_MONTH_MASK;
+    *year = (fat_date >> FAT_DATE_YEAR_SHIFT) & FAT_DATE_YEAR_MASK;
+    *year = *year + FAT_DATE_YEAR_OFFSET;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_to_fat_time: Convert h/m/s to FAT time
+//-----------------------------------------------------------------------------
+uint16 fatfs_convert_to_fat_time(int hours, int minutes, int seconds)
+{
+    uint16 fat_time = 0;
+
+    // Most FAT times are to a resolution of 2 seconds
+    seconds /= FAT_TIME_SECONDS_SCALE;
+
+    fat_time = (hours & FAT_TIME_HOURS_MASK) << FAT_TIME_HOURS_SHIFT;
+    fat_time|= (minutes & FAT_TIME_MINUTES_MASK) << FAT_TIME_MINUTES_SHIFT;
+    fat_time|= (seconds & FAT_TIME_SECONDS_MASK) << FAT_TIME_SECONDS_SHIFT;
+
+    return fat_time;
+}
+//-----------------------------------------------------------------------------
+// fatfs_convert_to_fat_date: Convert d/m/y to FAT date
+//-----------------------------------------------------------------------------
+uint16 fatfs_convert_to_fat_date(int day, int month, int year)
+{
+    uint16 fat_date = 0;
+
+    // FAT dates are relative to 1980
+    if (year >= FAT_DATE_YEAR_OFFSET)
+        year -= FAT_DATE_YEAR_OFFSET;
+
+    fat_date = (day & FAT_DATE_DAY_MASK) << FAT_DATE_DAY_SHIFT;
+    fat_date|= (month & FAT_DATE_MONTH_MASK) << FAT_DATE_MONTH_SHIFT;
+    fat_date|= (year & FAT_DATE_YEAR_MASK) << FAT_DATE_YEAR_SHIFT;
+
+    return fat_date;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_print_sector:
+//-----------------------------------------------------------------------------
+#ifdef FATFS_DEBUG
+void fatfs_print_sector(uint32 sector, uint8 *data)
+{
+    int i;
+    int j;
+
+    FAT_PRINTF(("Sector %d:\n", sector));
+
+    for (i=0;i<FAT_SECTOR_SIZE;i++)
+    {
+        if (!((i) % 16))
+        {
+            FAT_PRINTF(("  %04d: ", i));
+        }
+
+        FAT_PRINTF(("%02x", data[i]));
+        if (!((i+1) % 4))
+        {
+            FAT_PRINTF((" "));
+        }
+
+        if (!((i+1) % 16))
+        {
+            FAT_PRINTF(("   "));
+            for (j=0;j<16;j++)
+            {
+                char ch = data[i-15+j];
+
+                // Is printable?
+                if (ch > 31 && ch < 127)
+                {
+                    FAT_PRINTF(("%c", ch));
+                }
+                else
+                {
+                    FAT_PRINTF(("."));
+                }
+            }
+
+            FAT_PRINTF(("\n"));
+        }
+    }
+}
+#endif
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_misc.h b/vtoyjump/vtoyjump/fat_io_lib/fat_misc.h
new file mode 100644 (file)
index 0000000..0c02634
--- /dev/null
@@ -0,0 +1,63 @@
+#ifndef __FAT_MISC_H__
+#define __FAT_MISC_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Defines
+//-----------------------------------------------------------------------------
+#define MAX_LONGFILENAME_ENTRIES    20
+#define MAX_LFN_ENTRY_LENGTH        13
+
+//-----------------------------------------------------------------------------
+// Macros
+//-----------------------------------------------------------------------------
+#define GET_32BIT_WORD(buffer, location)    ( ((uint32)buffer[location+3]<<24) + ((uint32)buffer[location+2]<<16) + ((uint32)buffer[location+1]<<8) + (uint32)buffer[location+0] )
+#define GET_16BIT_WORD(buffer, location)    ( ((uint16)buffer[location+1]<<8) + (uint16)buffer[location+0] )
+
+#define SET_32BIT_WORD(buffer, location, value)    { buffer[location+0] = (uint8)((value)&0xFF); \
+                                                  buffer[location+1] = (uint8)((value>>8)&0xFF); \
+                                                  buffer[location+2] = (uint8)((value>>16)&0xFF); \
+                                                  buffer[location+3] = (uint8)((value>>24)&0xFF); }
+
+#define SET_16BIT_WORD(buffer, location, value)    { buffer[location+0] = (uint8)((value)&0xFF); \
+                                                  buffer[location+1] = (uint8)((value>>8)&0xFF); }
+
+//-----------------------------------------------------------------------------
+// Structures
+//-----------------------------------------------------------------------------
+struct lfn_cache
+{
+#if FATFS_INC_LFN_SUPPORT
+    // Long File Name Structure (max 260 LFN length)
+    uint8 String[MAX_LONGFILENAME_ENTRIES][MAX_LFN_ENTRY_LENGTH];
+    uint8 Null;
+#endif
+    uint8 no_of_strings;
+};
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void    fatfs_lfn_cache_init(struct lfn_cache *lfn, int wipeTable);
+void    fatfs_lfn_cache_entry(struct lfn_cache *lfn, uint8 *entryBuffer);
+char*   fatfs_lfn_cache_get(struct lfn_cache *lfn);
+int     fatfs_entry_lfn_text(struct fat_dir_entry *entry);
+int     fatfs_entry_lfn_invalid(struct fat_dir_entry *entry);
+int     fatfs_entry_lfn_exists(struct lfn_cache *lfn, struct fat_dir_entry *entry);
+int     fatfs_entry_sfn_only(struct fat_dir_entry *entry);
+int     fatfs_entry_is_dir(struct fat_dir_entry *entry);
+int     fatfs_entry_is_file(struct fat_dir_entry *entry);
+int     fatfs_lfn_entries_required(char *filename);
+void    fatfs_filename_to_lfn(char *filename, uint8 *buffer, int entry, uint8 sfnChk);
+void    fatfs_sfn_create_entry(char *shortfilename, uint32 size, uint32 startCluster, struct fat_dir_entry *entry, int dir);
+int     fatfs_lfn_create_sfn(char *sfn_output, char *filename);
+int     fatfs_lfn_generate_tail(char *sfn_output, char *sfn_input, uint32 tailNum);
+void    fatfs_convert_from_fat_time(uint16 fat_time, int *hours, int *minutes, int *seconds);
+void    fatfs_convert_from_fat_date(uint16 fat_date, int *day, int *month, int *year);
+uint16  fatfs_convert_to_fat_time(int hours, int minutes, int seconds);
+uint16  fatfs_convert_to_fat_date(int day, int month, int year);
+void    fatfs_print_sector(uint32 sector, uint8 *data);
+
+#endif
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_opts.h b/vtoyjump/vtoyjump/fat_io_lib/fat_opts.h
new file mode 100644 (file)
index 0000000..6c4728a
--- /dev/null
@@ -0,0 +1,91 @@
+#ifndef __FAT_OPTS_H__
+#define __FAT_OPTS_H__
+
+#ifdef FATFS_USE_CUSTOM_OPTS_FILE
+    #include "fat_custom.h"
+#endif
+
+//-------------------------------------------------------------
+// Configuration
+//-------------------------------------------------------------
+
+// Is the processor little endian (1) or big endian (0)
+#ifndef FATFS_IS_LITTLE_ENDIAN
+    #define FATFS_IS_LITTLE_ENDIAN          1
+#endif
+
+// Max filename Length
+#ifndef FATFS_MAX_LONG_FILENAME
+    #define FATFS_MAX_LONG_FILENAME         260
+#endif
+
+// Max open files (reduce to lower memory requirements)
+#ifndef FATFS_MAX_OPEN_FILES
+    #define FATFS_MAX_OPEN_FILES            2
+#endif
+
+// Number of sectors per FAT_BUFFER (min 1)
+#ifndef FAT_BUFFER_SECTORS
+    #define FAT_BUFFER_SECTORS              1
+#endif
+
+// Max FAT sectors to buffer (min 1)
+// (mem used is FAT_BUFFERS * FAT_BUFFER_SECTORS * FAT_SECTOR_SIZE)
+#ifndef FAT_BUFFERS
+    #define FAT_BUFFERS                     1
+#endif
+
+// Size of cluster chain cache (can be undefined)
+// Mem used = FAT_CLUSTER_CACHE_ENTRIES * 4 * 2
+// Improves access speed considerably
+//#define FAT_CLUSTER_CACHE_ENTRIES         128
+
+// Include support for writing files (1 / 0)?
+#ifndef FATFS_INC_WRITE_SUPPORT
+    #define FATFS_INC_WRITE_SUPPORT         1
+#endif
+
+// Support long filenames (1 / 0)?
+// (if not (0) only 8.3 format is supported)
+#ifndef FATFS_INC_LFN_SUPPORT
+    #define FATFS_INC_LFN_SUPPORT           1
+#endif
+
+// Support directory listing (1 / 0)?
+#ifndef FATFS_DIR_LIST_SUPPORT
+    #define FATFS_DIR_LIST_SUPPORT          1
+#endif
+
+// Support time/date (1 / 0)?
+#ifndef FATFS_INC_TIME_DATE_SUPPORT
+    #define FATFS_INC_TIME_DATE_SUPPORT     0
+#endif
+
+// Include support for formatting disks (1 / 0)?
+#ifndef FATFS_INC_FORMAT_SUPPORT
+    #define FATFS_INC_FORMAT_SUPPORT        1
+#endif
+
+// Sector size used
+#define FAT_SECTOR_SIZE                     512
+
+// Printf output (directory listing / debug)
+#ifndef FAT_PRINTF
+    // Don't include stdio, but there is a printf function available
+    #ifdef FAT_PRINTF_NOINC_STDIO
+        extern int printf(const char* ctrl1, ... );
+        #define FAT_PRINTF(a)               printf a
+    // Include stdio to use printf
+    #else
+        #include <stdio.h>
+        void Log(const char *fmt, ...);
+        #define FAT_PRINTF(a)               Log a
+    #endif
+#endif
+
+// Time/Date support requires time.h
+#if FATFS_INC_TIME_DATE_SUPPORT
+    #include <time.h>
+#endif
+
+#endif
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_string.c b/vtoyjump/vtoyjump/fat_io_lib/fat_string.c
new file mode 100644 (file)
index 0000000..edf1db9
--- /dev/null
@@ -0,0 +1,514 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//                            FAT16/32 File IO Library
+//                                    V2.6
+//                              Ultra-Embedded.com
+//                            Copyright 2003 - 2012
+//
+//                         Email: admin@ultra-embedded.com
+//
+//                                License: GPL
+//   If you would like a version with a more permissive license for use in
+//   closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include <string.h>
+#include <assert.h>
+#include "fat_string.h"
+
+//-----------------------------------------------------------------------------
+// fatfs_total_path_levels: Take a filename and path and count the sub levels
+// of folders. E.g. C:\folder\file.zip = 1 level
+// Acceptable input formats are:
+//        c:\folder\file.zip
+//        /dev/etc/samba.conf
+// Returns: -1 = Error, 0 or more = Ok
+//-----------------------------------------------------------------------------
+int fatfs_total_path_levels(char *path)
+{
+    int levels = 0;
+    char expectedchar;
+
+    if (!path)
+        return -1;
+
+    // Acceptable formats:
+    //  c:\folder\file.zip
+    //  /dev/etc/samba.conf
+    if (*path == '/')
+    {
+        expectedchar = '/';
+        path++;
+    }
+    else if (path[1] == ':' || path[2] == '\\')
+    {
+        expectedchar = '\\';
+        path += 3;
+    }
+    else
+        return -1;
+
+    // Count levels in path string
+    while (*path)
+    {
+        // Fast forward through actual subdir text to next slash
+        for (; *path; )
+        {
+            // If slash detected escape from for loop
+            if (*path == expectedchar) { path++; break; }
+            path++;
+        }
+
+        // Increase number of subdirs founds
+        levels++;
+    }
+
+    // Subtract the file itself
+    return levels-1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_substring: Get a substring from 'path' which contains the folder
+// (or file) at the specified level.
+// E.g. C:\folder\file.zip : Level 0 = C:\folder, Level 1 = file.zip
+// Returns: -1 = Error, 0 = Ok
+//-----------------------------------------------------------------------------
+int fatfs_get_substring(char *path, int levelreq, char *output, int max_len)
+{
+    int i;
+    int pathlen=0;
+    int levels=0;
+    int copypnt=0;
+    char expectedchar;
+
+    if (!path || max_len <= 0)
+        return -1;
+
+    // Acceptable formats:
+    //  c:\folder\file.zip
+    //  /dev/etc/samba.conf
+    if (*path == '/')
+    {
+        expectedchar = '/';
+        path++;
+    }
+    else if (path[1] == ':' || path[2] == '\\')
+    {
+        expectedchar = '\\';
+        path += 3;
+    }
+    else
+        return -1;
+
+    // Get string length of path
+    pathlen = (int)strlen (path);
+
+    // Loop through the number of times as characters in 'path'
+    for (i = 0; i<pathlen; i++)
+    {
+        // If a '\' is found then increase level
+        if (*path == expectedchar) levels++;
+
+        // If correct level and the character is not a '\' or '/' then copy text to 'output'
+        if ( (levels == levelreq) && (*path != expectedchar) && (copypnt < (max_len-1)))
+            output[copypnt++] = *path;
+
+        // Increment through path string
+        path++;
+    }
+
+    // Null Terminate
+    output[copypnt] = '\0';
+
+    // If a string was copied return 0 else return 1
+    if (output[0] != '\0')
+        return 0;    // OK
+    else
+        return -1;    // Error
+}
+//-----------------------------------------------------------------------------
+// fatfs_split_path: Full path contains the passed in string.
+// Returned is the path string and file Name string
+// E.g. C:\folder\file.zip -> path = C:\folder  filename = file.zip
+// E.g. C:\file.zip -> path = [blank]  filename = file.zip
+//-----------------------------------------------------------------------------
+int fatfs_split_path(char *full_path, char *path, int max_path, char *filename, int max_filename)
+{
+    int strindex;
+
+    // Count the levels to the filepath
+    int levels = fatfs_total_path_levels(full_path);
+    if (levels == -1)
+        return -1;
+
+    // Get filename part of string
+    if (fatfs_get_substring(full_path, levels, filename, max_filename) != 0)
+        return -1;
+
+    // If root file
+    if (levels == 0)
+        path[0] = '\0';
+    else
+    {
+        strindex = (int)strlen(full_path) - (int)strlen(filename);
+        if (strindex > max_path)
+            strindex = max_path;
+
+        memcpy(path, full_path, strindex);
+        path[strindex-1] = '\0';
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// FileString_StrCmpNoCase: Compare two strings case with case sensitivity
+//-----------------------------------------------------------------------------
+static int FileString_StrCmpNoCase(char *s1, char *s2, int n)
+{
+    int diff;
+    char a,b;
+
+    while (n--)
+    {
+        a = *s1;
+        b = *s2;
+
+        // Make lower case if uppercase
+        if ((a>='A') && (a<='Z'))
+            a+= 32;
+        if ((b>='A') && (b<='Z'))
+            b+= 32;
+
+        diff = a - b;
+
+        // If different
+        if (diff)
+            return diff;
+
+        // If run out of strings
+        if ( (*s1 == 0) || (*s2 == 0) )
+            break;
+
+        s1++;
+        s2++;
+    }
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// FileString_GetExtension: Get index to extension within filename
+// Returns -1 if not found or index otherwise
+//-----------------------------------------------------------------------------
+static int FileString_GetExtension(char *str)
+{
+    int dotPos = -1;
+    char *strSrc = str;
+
+    // Find last '.' in string (if at all)
+    while (*strSrc)
+    {
+        if (*strSrc=='.')
+            dotPos = (int)(strSrc-str);
+
+        strSrc++;
+    }
+
+    return dotPos;
+}
+//-----------------------------------------------------------------------------
+// FileString_TrimLength: Get length of string excluding trailing spaces
+// Returns -1 if not found or index otherwise
+//-----------------------------------------------------------------------------
+static int FileString_TrimLength(char *str, int strLen)
+{
+    int length = strLen;
+    char *strSrc = str+strLen-1;
+
+    // Find last non white space
+    while (strLen != 0)
+    {
+        if (*strSrc == ' ')
+            length = (int)(strSrc - str);
+        else
+            break;
+
+        strSrc--;
+        strLen--;
+    }
+
+    return length;
+}
+//-----------------------------------------------------------------------------
+// fatfs_compare_names: Compare two filenames (without copying or changing origonals)
+// Returns 1 if match, 0 if not
+//-----------------------------------------------------------------------------
+int fatfs_compare_names(char* strA, char* strB)
+{
+    char *ext1 = NULL;
+    char *ext2 = NULL;
+    int ext1Pos, ext2Pos;
+    int file1Len, file2Len;
+
+    // Get both files extension
+    ext1Pos = FileString_GetExtension(strA);
+    ext2Pos = FileString_GetExtension(strB);
+
+    // NOTE: Extension position can be different for matching
+    // filename if trailing space are present before it!
+    // Check that if one has an extension, so does the other
+    if ((ext1Pos==-1) && (ext2Pos!=-1))
+        return 0;
+    if ((ext2Pos==-1) && (ext1Pos!=-1))
+        return 0;
+
+    // If they both have extensions, compare them
+    if (ext1Pos!=-1)
+    {
+        // Set pointer to start of extension
+        ext1 = strA+ext1Pos+1;
+        ext2 = strB+ext2Pos+1;
+
+        // Verify that the file extension lengths match!
+        if (strlen(ext1) != strlen(ext2))
+            return 0;
+
+        // If they dont match
+        if (FileString_StrCmpNoCase(ext1, ext2, (int)strlen(ext1))!=0)
+            return 0;
+
+        // Filelength is upto extensions
+        file1Len = ext1Pos;
+        file2Len = ext2Pos;
+    }
+    // No extensions
+    else
+    {
+        // Filelength is actual filelength
+        file1Len = (int)strlen(strA);
+        file2Len = (int)strlen(strB);
+    }
+
+    // Find length without trailing spaces (before ext)
+    file1Len = FileString_TrimLength(strA, file1Len);
+    file2Len = FileString_TrimLength(strB, file2Len);
+
+    // Check the file lengths match
+    if (file1Len!=file2Len)
+        return 0;
+
+    // Compare main part of filenames
+    if (FileString_StrCmpNoCase(strA, strB, file1Len)!=0)
+        return 0;
+    else
+        return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_string_ends_with_slash: Does the string end with a slash (\ or /)
+//-----------------------------------------------------------------------------
+int fatfs_string_ends_with_slash(char *path)
+{
+    if (path)
+    {
+        while (*path)
+        {
+            // Last character?
+            if (!(*(path+1)))
+            {
+                if (*path == '\\' || *path == '/')
+                    return 1;
+            }
+
+            path++;
+        }
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_sfn_display_name: Get display name for SFN entry
+//-----------------------------------------------------------------------------
+int fatfs_get_sfn_display_name(char* out, char* in)
+{
+    int len = 0;
+    while (*in && len <= 11)
+    {
+        char a = *in++;
+
+        if (a == ' ')
+            continue;
+        // Make lower case if uppercase
+        else if ((a>='A') && (a<='Z'))
+            a+= 32;
+
+        *out++ = a;
+        len++;
+    }
+
+    *out = '\0';
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_get_extension: Get extension of filename passed in 'filename'.
+// Returned extension is always lower case.
+// Returns: 1 if ok, 0 if not.
+//-----------------------------------------------------------------------------
+int fatfs_get_extension(char* filename, char* out, int maxlen)
+{
+    int len = 0;
+
+    // Get files extension offset
+    int ext_pos = FileString_GetExtension(filename);
+
+    if (ext_pos > 0 && out && maxlen)
+    {
+        filename += ext_pos + 1;
+
+        while (*filename && len < (maxlen-1))
+        {
+            char a = *filename++;
+
+            // Make lowercase if uppercase
+            if ((a>='A') && (a<='Z'))
+                a+= 32;
+
+            *out++ = a;
+            len++;
+        }
+
+        *out = '\0';
+        return 1;
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_create_path_string: Append path & filename to create file path string.
+// Returns: 1 if ok, 0 if not.
+//-----------------------------------------------------------------------------
+int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen)
+{
+    int len = 0;
+    char last = 0;
+    char seperator = '/';
+
+    if (path && filename && out && maxlen > 0)
+    {
+        while (*path && len < (maxlen-2))
+        {
+            last = *path++;
+            if (last == '\\')
+                seperator = '\\';
+            *out++ = last;
+            len++;
+        }
+
+        // Add a seperator if trailing one not found
+        if (last != '\\' && last != '/')
+            *out++ = seperator;
+
+        while (*filename && len < (maxlen-1))
+        {
+            *out++ = *filename++;
+            len++;
+        }
+
+        *out = '\0';
+
+        return 1;
+    }
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// Test Bench
+//-----------------------------------------------------------------------------
+#ifdef FAT_STRING_TESTBENCH
+void main(void)
+{
+    char output[255];
+    char output2[255];
+
+    assert(fatfs_total_path_levels("C:\\folder\\file.zip") == 1);
+    assert(fatfs_total_path_levels("C:\\file.zip") == 0);
+    assert(fatfs_total_path_levels("C:\\folder\\folder2\\file.zip") == 2);
+    assert(fatfs_total_path_levels("C:\\") == -1);
+    assert(fatfs_total_path_levels("") == -1);
+    assert(fatfs_total_path_levels("/dev/etc/file.zip") == 2);
+    assert(fatfs_total_path_levels("/dev/file.zip") == 1);
+
+    assert(fatfs_get_substring("C:\\folder\\file.zip", 0, output, sizeof(output)) == 0);
+    assert(strcmp(output, "folder") == 0);
+
+    assert(fatfs_get_substring("C:\\folder\\file.zip", 1, output, sizeof(output)) == 0);
+    assert(strcmp(output, "file.zip") == 0);
+
+    assert(fatfs_get_substring("/dev/etc/file.zip", 0, output, sizeof(output)) == 0);
+    assert(strcmp(output, "dev") == 0);
+
+    assert(fatfs_get_substring("/dev/etc/file.zip", 1, output, sizeof(output)) == 0);
+    assert(strcmp(output, "etc") == 0);
+
+    assert(fatfs_get_substring("/dev/etc/file.zip", 2, output, sizeof(output)) == 0);
+    assert(strcmp(output, "file.zip") == 0);
+
+    assert(fatfs_split_path("C:\\folder\\file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+    assert(strcmp(output, "C:\\folder") == 0);
+    assert(strcmp(output2, "file.zip") == 0);
+
+    assert(fatfs_split_path("C:\\file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+    assert(output[0] == 0);
+    assert(strcmp(output2, "file.zip") == 0);
+
+    assert(fatfs_split_path("/dev/etc/file.zip", output, sizeof(output), output2, sizeof(output2)) == 0);
+    assert(strcmp(output, "/dev/etc") == 0);
+    assert(strcmp(output2, "file.zip") == 0);
+
+    assert(FileString_GetExtension("C:\\file.zip") == strlen("C:\\file"));
+    assert(FileString_GetExtension("C:\\file.zip.ext") == strlen("C:\\file.zip"));
+    assert(FileString_GetExtension("C:\\file.zip.") == strlen("C:\\file.zip"));
+
+    assert(FileString_TrimLength("C:\\file.zip", strlen("C:\\file.zip")) == strlen("C:\\file.zip"));
+    assert(FileString_TrimLength("C:\\file.zip   ", strlen("C:\\file.zip   ")) == strlen("C:\\file.zip"));
+    assert(FileString_TrimLength("   ", strlen("   ")) == 0);
+
+    assert(fatfs_compare_names("C:\\file.ext", "C:\\file.ext") == 1);
+    assert(fatfs_compare_names("C:\\file2.ext", "C:\\file.ext") == 0);
+    assert(fatfs_compare_names("C:\\file  .ext", "C:\\file.ext") == 1);
+    assert(fatfs_compare_names("C:\\file  .ext", "C:\\file2.ext") == 0);
+
+    assert(fatfs_string_ends_with_slash("C:\\folder") == 0);
+    assert(fatfs_string_ends_with_slash("C:\\folder\\") == 1);
+    assert(fatfs_string_ends_with_slash("/path") == 0);
+    assert(fatfs_string_ends_with_slash("/path/a") == 0);
+    assert(fatfs_string_ends_with_slash("/path/") == 1);
+
+    assert(fatfs_get_extension("/mypath/file.wav", output, 4) == 1);
+    assert(strcmp(output, "wav") == 0);
+    assert(fatfs_get_extension("/mypath/file.WAV", output, 4) == 1);
+    assert(strcmp(output, "wav") == 0);
+    assert(fatfs_get_extension("/mypath/file.zip", output, 4) == 1);
+    assert(strcmp(output, "ext") != 0);
+
+    assert(fatfs_create_path_string("/mydir1", "myfile.txt", output, sizeof(output)) == 1);
+    assert(strcmp(output, "/mydir1/myfile.txt") == 0);
+    assert(fatfs_create_path_string("/mydir2/", "myfile2.txt", output, sizeof(output)) == 1);
+    assert(strcmp(output, "/mydir2/myfile2.txt") == 0);
+    assert(fatfs_create_path_string("C:\\mydir3", "myfile3.txt", output, sizeof(output)) == 1);
+    assert(strcmp(output, "C:\\mydir3\\myfile3.txt") == 0);
+}
+#endif
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_string.h b/vtoyjump/vtoyjump/fat_io_lib/fat_string.h
new file mode 100644 (file)
index 0000000..90ca8e0
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __FILESTRING_H__
+#define __FILESTRING_H__
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_total_path_levels(char *path);
+int fatfs_get_substring(char *Path, int levelreq, char *output, int max_len);
+int fatfs_split_path(char *FullPath, char *Path, int max_path, char *FileName, int max_filename);
+int fatfs_compare_names(char* strA, char* strB);
+int fatfs_string_ends_with_slash(char *path);
+int fatfs_get_sfn_display_name(char* out, char* in);
+int fatfs_get_extension(char* filename, char* out, int maxlen);
+int fatfs_create_path_string(char* path, char *filename, char* out, int maxlen);
+
+#ifndef NULL
+    #define NULL 0
+#endif
+
+#endif
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_table.c b/vtoyjump/vtoyjump/fat_io_lib/fat_table.c
new file mode 100644 (file)
index 0000000..35dcc7b
--- /dev/null
@@ -0,0 +1,478 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//                            FAT16/32 File IO Library
+//                                    V2.6
+//                              Ultra-Embedded.com
+//                            Copyright 2003 - 2012
+//
+//                         Email: admin@ultra-embedded.com
+//
+//                                License: GPL
+//   If you would like a version with a more permissive license for use in
+//   closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+
+#ifndef FAT_BUFFERS
+    #define FAT_BUFFERS 1
+#endif
+
+#ifndef FAT_BUFFER_SECTORS
+    #define FAT_BUFFER_SECTORS 1
+#endif
+
+#if FAT_BUFFERS < 1 || FAT_BUFFER_SECTORS < 1
+    #error "FAT_BUFFERS & FAT_BUFFER_SECTORS must be at least 1"
+#endif
+
+//-----------------------------------------------------------------------------
+//                            FAT Sector Buffer
+//-----------------------------------------------------------------------------
+#define FAT32_GET_32BIT_WORD(pbuf, location)        ( GET_32BIT_WORD(pbuf->ptr, location) )
+#define FAT32_SET_32BIT_WORD(pbuf, location, value) { SET_32BIT_WORD(pbuf->ptr, location, value); pbuf->dirty = 1; }
+#define FAT16_GET_16BIT_WORD(pbuf, location)        ( GET_16BIT_WORD(pbuf->ptr, location) )
+#define FAT16_SET_16BIT_WORD(pbuf, location, value) { SET_16BIT_WORD(pbuf->ptr, location, value); pbuf->dirty = 1; }
+
+//-----------------------------------------------------------------------------
+// fatfs_fat_init:
+//-----------------------------------------------------------------------------
+void fatfs_fat_init(struct fatfs *fs)
+{
+    int i;
+
+    // FAT buffer chain head
+    fs->fat_buffer_head = NULL;
+
+    for (i=0;i<FAT_BUFFERS;i++)
+    {
+        // Initialise buffers to invalid
+        fs->fat_buffers[i].address = FAT32_INVALID_CLUSTER;
+        fs->fat_buffers[i].dirty = 0;
+        memset(fs->fat_buffers[i].sector, 0x00, sizeof(fs->fat_buffers[i].sector));
+        fs->fat_buffers[i].ptr = NULL;
+
+        // Add to head of queue
+        fs->fat_buffers[i].next = fs->fat_buffer_head;
+        fs->fat_buffer_head = &fs->fat_buffers[i];
+    }
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_writeback: Writeback 'dirty' FAT sectors to disk
+//-----------------------------------------------------------------------------
+static int fatfs_fat_writeback(struct fatfs *fs, struct fat_buffer *pcur)
+{
+    if (pcur)
+    {
+        // Writeback sector if changed
+        if (pcur->dirty)
+        {
+            if (fs->disk_io.write_media)
+            {
+                uint32 sectors = FAT_BUFFER_SECTORS;
+                uint32 offset = pcur->address - fs->fat_begin_lba;
+
+                // Limit to sectors used for the FAT
+                if ((offset + FAT_BUFFER_SECTORS) <= fs->fat_sectors)
+                    sectors = FAT_BUFFER_SECTORS;
+                else
+                    sectors = fs->fat_sectors - offset;
+
+                if (!fs->disk_io.write_media(pcur->address, pcur->sector, sectors))
+                    return 0;
+            }
+
+            pcur->dirty = 0;
+        }
+
+        return 1;
+    }
+    else
+        return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_read_sector: Read a FAT sector
+//-----------------------------------------------------------------------------
+static struct fat_buffer *fatfs_fat_read_sector(struct fatfs *fs, uint32 sector)
+{
+    struct fat_buffer *last = NULL;
+    struct fat_buffer *pcur = fs->fat_buffer_head;
+
+    // Itterate through sector buffer list
+    while (pcur)
+    {
+        // Sector within this buffer?
+        if ((sector >= pcur->address) && (sector < (pcur->address + FAT_BUFFER_SECTORS)))
+            break;
+
+        // End of list?
+        if (pcur->next == NULL)
+        {
+            // Remove buffer from list
+            if (last)
+                last->next = NULL;
+            // We the first and last buffer in the chain?
+            else
+                fs->fat_buffer_head = NULL;
+        }
+
+        last = pcur;
+        pcur = pcur->next;
+    }
+
+    // We found the sector already in FAT buffer chain
+    if (pcur)
+    {
+        pcur->ptr = (uint8 *)(pcur->sector + ((sector - pcur->address) * FAT_SECTOR_SIZE));
+        return pcur;
+    }
+
+    // Else, we removed the last item from the list
+    pcur = last;
+
+    // Add to start of sector buffer list (now newest sector)
+    pcur->next = fs->fat_buffer_head;
+    fs->fat_buffer_head = pcur;
+
+    // Writeback sector if changed
+    if (pcur->dirty)
+        if (!fatfs_fat_writeback(fs, pcur))
+            return 0;
+
+    // Address is now new sector
+    pcur->address = sector;
+
+    // Read next sector
+    if (!fs->disk_io.read_media(pcur->address, pcur->sector, FAT_BUFFER_SECTORS))
+    {
+        // Read failed, invalidate buffer address
+        pcur->address = FAT32_INVALID_CLUSTER;
+        return NULL;
+    }
+
+    pcur->ptr = pcur->sector;
+    return pcur;
+}
+//-----------------------------------------------------------------------------
+// fatfs_fat_purge: Purge 'dirty' FAT sectors to disk
+//-----------------------------------------------------------------------------
+int fatfs_fat_purge(struct fatfs *fs)
+{
+    struct fat_buffer *pcur = fs->fat_buffer_head;
+
+    // Itterate through sector buffer list
+    while (pcur)
+    {
+        // Writeback sector if changed
+        if (pcur->dirty)
+            if (!fatfs_fat_writeback(fs, pcur))
+                return 0;
+
+        pcur = pcur->next;
+    }
+
+    return 1;
+}
+
+//-----------------------------------------------------------------------------
+//                        General FAT Table Operations
+//-----------------------------------------------------------------------------
+
+//-----------------------------------------------------------------------------
+// fatfs_find_next_cluster: Return cluster number of next cluster in chain by
+// reading FAT table and traversing it. Return 0xffffffff for end of chain.
+//-----------------------------------------------------------------------------
+uint32 fatfs_find_next_cluster(struct fatfs *fs, uint32 current_cluster)
+{
+    uint32 fat_sector_offset, position;
+    uint32 nextcluster;
+    struct fat_buffer *pbuf;
+
+    // Why is '..' labelled with cluster 0 when it should be 2 ??
+    if (current_cluster == 0)
+        current_cluster = 2;
+
+    // Find which sector of FAT table to read
+    if (fs->fat_type == FAT_TYPE_16)
+        fat_sector_offset = current_cluster / 256;
+    else
+        fat_sector_offset = current_cluster / 128;
+
+    // Read FAT sector into buffer
+    pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
+    if (!pbuf)
+        return (FAT32_LAST_CLUSTER);
+
+    if (fs->fat_type == FAT_TYPE_16)
+    {
+        // Find 32 bit entry of current sector relating to cluster number
+        position = (current_cluster - (fat_sector_offset * 256)) * 2;
+
+        // Read Next Clusters value from Sector Buffer
+        nextcluster = FAT16_GET_16BIT_WORD(pbuf, (uint16)position);
+
+        // If end of chain found
+        if (nextcluster >= 0xFFF8 && nextcluster <= 0xFFFF)
+            return (FAT32_LAST_CLUSTER);
+    }
+    else
+    {
+        // Find 32 bit entry of current sector relating to cluster number
+        position = (current_cluster - (fat_sector_offset * 128)) * 4;
+
+        // Read Next Clusters value from Sector Buffer
+        nextcluster = FAT32_GET_32BIT_WORD(pbuf, (uint16)position);
+
+        // Mask out MS 4 bits (its 28bit addressing)
+        nextcluster = nextcluster & 0x0FFFFFFF;
+
+        // If end of chain found
+        if (nextcluster >= 0x0FFFFFF8 && nextcluster <= 0x0FFFFFFF)
+            return (FAT32_LAST_CLUSTER);
+    }
+
+    // Else return next cluster
+    return (nextcluster);
+}
+//-----------------------------------------------------------------------------
+// fatfs_set_fs_info_next_free_cluster: Write the next free cluster to the FSINFO table
+//-----------------------------------------------------------------------------
+void fatfs_set_fs_info_next_free_cluster(struct fatfs *fs, uint32 newValue)
+{
+    if (fs->fat_type == FAT_TYPE_16)
+        ;
+    else
+    {
+        // Load sector to change it
+        struct fat_buffer *pbuf = fatfs_fat_read_sector(fs, fs->lba_begin+fs->fs_info_sector);
+        if (!pbuf)
+            return ;
+
+        // Change
+        FAT32_SET_32BIT_WORD(pbuf, 492, newValue);
+        fs->next_free_cluster = newValue;
+
+        // Write back FSINFO sector to disk
+        if (fs->disk_io.write_media)
+            fs->disk_io.write_media(pbuf->address, pbuf->sector, 1);
+
+        // Invalidate cache entry
+        pbuf->address = FAT32_INVALID_CLUSTER;
+        pbuf->dirty = 0;
+    }
+}
+//-----------------------------------------------------------------------------
+// fatfs_find_blank_cluster: Find a free cluster entry by reading the FAT
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_find_blank_cluster(struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster)
+{
+    uint32 fat_sector_offset, position;
+    uint32 nextcluster;
+    uint32 current_cluster = start_cluster;
+    struct fat_buffer *pbuf;
+
+    do
+    {
+        // Find which sector of FAT table to read
+        if (fs->fat_type == FAT_TYPE_16)
+            fat_sector_offset = current_cluster / 256;
+        else
+            fat_sector_offset = current_cluster / 128;
+
+        if ( fat_sector_offset < fs->fat_sectors)
+        {
+            // Read FAT sector into buffer
+            pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
+            if (!pbuf)
+                return 0;
+
+            if (fs->fat_type == FAT_TYPE_16)
+            {
+                // Find 32 bit entry of current sector relating to cluster number
+                position = (current_cluster - (fat_sector_offset * 256)) * 2;
+
+                // Read Next Clusters value from Sector Buffer
+                nextcluster = FAT16_GET_16BIT_WORD(pbuf, (uint16)position);
+            }
+            else
+            {
+                // Find 32 bit entry of current sector relating to cluster number
+                position = (current_cluster - (fat_sector_offset * 128)) * 4;
+
+                // Read Next Clusters value from Sector Buffer
+                nextcluster = FAT32_GET_32BIT_WORD(pbuf, (uint16)position);
+
+                // Mask out MS 4 bits (its 28bit addressing)
+                nextcluster = nextcluster & 0x0FFFFFFF;
+            }
+
+            if (nextcluster !=0 )
+                current_cluster++;
+        }
+        else
+            // Otherwise, run out of FAT sectors to check...
+            return 0;
+    }
+    while (nextcluster != 0x0);
+
+    // Found blank entry
+    *free_cluster = current_cluster;
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_fat_set_cluster: Set a cluster link in the chain. NOTE: Immediate
+// write (slow).
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_fat_set_cluster(struct fatfs *fs, uint32 cluster, uint32 next_cluster)
+{
+    struct fat_buffer *pbuf;
+    uint32 fat_sector_offset, position;
+
+    // Find which sector of FAT table to read
+    if (fs->fat_type == FAT_TYPE_16)
+        fat_sector_offset = cluster / 256;
+    else
+        fat_sector_offset = cluster / 128;
+
+    // Read FAT sector into buffer
+    pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba+fat_sector_offset);
+    if (!pbuf)
+        return 0;
+
+    if (fs->fat_type == FAT_TYPE_16)
+    {
+        // Find 16 bit entry of current sector relating to cluster number
+        position = (cluster - (fat_sector_offset * 256)) * 2;
+
+        // Write Next Clusters value to Sector Buffer
+        FAT16_SET_16BIT_WORD(pbuf, (uint16)position, ((uint16)next_cluster));
+    }
+    else
+    {
+        // Find 32 bit entry of current sector relating to cluster number
+        position = (cluster - (fat_sector_offset * 128)) * 4;
+
+        // Write Next Clusters value to Sector Buffer
+        FAT32_SET_32BIT_WORD(pbuf, (uint16)position, next_cluster);
+    }
+
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_free_cluster_chain: Follow a chain marking each element as free
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_free_cluster_chain(struct fatfs *fs, uint32 start_cluster)
+{
+    uint32 last_cluster;
+    uint32 next_cluster = start_cluster;
+
+    // Loop until end of chain
+    while ( (next_cluster != FAT32_LAST_CLUSTER) && (next_cluster != 0x00000000) )
+    {
+        last_cluster = next_cluster;
+
+        // Find next link
+        next_cluster = fatfs_find_next_cluster(fs, next_cluster);
+
+        // Clear last link
+        fatfs_fat_set_cluster(fs, last_cluster, 0x00000000);
+    }
+
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_fat_add_cluster_to_chain: Follow a chain marking and then add a new entry
+// to the current tail.
+//-----------------------------------------------------------------------------
+#if FATFS_INC_WRITE_SUPPORT
+int fatfs_fat_add_cluster_to_chain(struct fatfs *fs, uint32 start_cluster, uint32 newEntry)
+{
+    uint32 last_cluster = FAT32_LAST_CLUSTER;
+    uint32 next_cluster = start_cluster;
+
+    if (start_cluster == FAT32_LAST_CLUSTER)
+        return 0;
+
+    // Loop until end of chain
+    while ( next_cluster != FAT32_LAST_CLUSTER )
+    {
+        last_cluster = next_cluster;
+
+        // Find next link
+        next_cluster = fatfs_find_next_cluster(fs, next_cluster);
+        if (!next_cluster)
+            return 0;
+    }
+
+    // Add link in for new cluster
+    fatfs_fat_set_cluster(fs, last_cluster, newEntry);
+
+    // Mark new cluster as end of chain
+    fatfs_fat_set_cluster(fs, newEntry, FAT32_LAST_CLUSTER);
+
+    return 1;
+}
+#endif
+//-----------------------------------------------------------------------------
+// fatfs_count_free_clusters:
+//-----------------------------------------------------------------------------
+uint32 fatfs_count_free_clusters(struct fatfs *fs)
+{
+    uint32 i,j;
+    uint32 count = 0;
+    struct fat_buffer *pbuf;
+
+    for (i = 0; i < fs->fat_sectors; i++)
+    {
+        // Read FAT sector into buffer
+        pbuf = fatfs_fat_read_sector(fs, fs->fat_begin_lba + i);
+        if (!pbuf)
+            break;
+
+        for (j = 0; j < FAT_SECTOR_SIZE; )
+        {
+            if (fs->fat_type == FAT_TYPE_16)
+            {
+                if (FAT16_GET_16BIT_WORD(pbuf, (uint16)j) == 0)
+                    count++;
+
+                j += 2;
+            }
+            else
+            {
+                if (FAT32_GET_32BIT_WORD(pbuf, (uint16)j) == 0)
+                    count++;
+
+                j += 4;
+            }
+        }
+    }
+
+    return count;
+}
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_table.h b/vtoyjump/vtoyjump/fat_io_lib/fat_table.h
new file mode 100644 (file)
index 0000000..ead75f3
--- /dev/null
@@ -0,0 +1,20 @@
+#ifndef __FAT_TABLE_H__
+#define __FAT_TABLE_H__
+
+#include "fat_opts.h"
+#include "fat_misc.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+void    fatfs_fat_init(struct fatfs *fs);
+int     fatfs_fat_purge(struct fatfs *fs);
+uint32  fatfs_find_next_cluster(struct fatfs *fs, uint32 current_cluster);
+void    fatfs_set_fs_info_next_free_cluster(struct fatfs *fs, uint32 newValue);
+int     fatfs_find_blank_cluster(struct fatfs *fs, uint32 start_cluster, uint32 *free_cluster);
+int     fatfs_fat_set_cluster(struct fatfs *fs, uint32 cluster, uint32 next_cluster);
+int     fatfs_fat_add_cluster_to_chain(struct fatfs *fs, uint32 start_cluster, uint32 newEntry);
+int     fatfs_free_cluster_chain(struct fatfs *fs, uint32 start_cluster);
+uint32  fatfs_count_free_clusters(struct fatfs *fs);
+
+#endif
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_types.h b/vtoyjump/vtoyjump/fat_io_lib/fat_types.h
new file mode 100644 (file)
index 0000000..5e2cca8
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef __FAT_TYPES_H__
+#define __FAT_TYPES_H__
+
+// Detect 64-bit compilation on GCC
+#if defined(__GNUC__) && defined(__SIZEOF_LONG__)
+    #if __SIZEOF_LONG__ == 8
+        #define FATFS_DEF_UINT32_AS_INT
+    #endif
+#endif
+
+//-------------------------------------------------------------
+// System specific types
+//-------------------------------------------------------------
+#ifndef FATFS_NO_DEF_TYPES
+    typedef unsigned char uint8;
+    typedef unsigned short uint16;
+
+    // If compiling on a 64-bit machine, use int as 32-bits
+    #ifdef FATFS_DEF_UINT32_AS_INT
+        typedef unsigned int uint32;
+    // Else for 32-bit machines & embedded systems, use long...
+    #else
+        typedef unsigned long uint32;
+    #endif
+#endif
+
+#ifndef NULL
+    #define NULL 0
+#endif
+
+//-------------------------------------------------------------
+// Endian Macros
+//-------------------------------------------------------------
+// FAT is little endian so big endian systems need to swap words
+
+// Little Endian - No swap required
+#if FATFS_IS_LITTLE_ENDIAN == 1
+
+    #define FAT_HTONS(n) (n)
+    #define FAT_HTONL(n) (n)
+
+// Big Endian - Swap required
+#else
+
+    #define FAT_HTONS(n) ((((uint16)((n) & 0xff)) << 8) | (((n) & 0xff00) >> 8))
+    #define FAT_HTONL(n) (((((uint32)(n) & 0xFF)) << 24) | \
+                    ((((uint32)(n) & 0xFF00)) << 8) | \
+                    ((((uint32)(n) & 0xFF0000)) >> 8) | \
+                    ((((uint32)(n) & 0xFF000000)) >> 24))
+
+#endif
+
+//-------------------------------------------------------------
+// Structure Packing Compile Options
+//-------------------------------------------------------------
+#ifdef __GNUC__
+    #define STRUCT_PACK
+    #define STRUCT_PACK_BEGIN
+    #define STRUCT_PACK_END
+    #define STRUCT_PACKED           __attribute__ ((packed))
+#else
+    // Other compilers may require other methods of packing structures
+    #define STRUCT_PACK
+    #define STRUCT_PACK_BEGIN
+    #define STRUCT_PACK_END
+    #define STRUCT_PACKED
+#endif
+
+#endif
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_write.c b/vtoyjump/vtoyjump/fat_io_lib/fat_write.c
new file mode 100644 (file)
index 0000000..8b12c4b
--- /dev/null
@@ -0,0 +1,373 @@
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+//                            FAT16/32 File IO Library
+//                                    V2.6
+//                              Ultra-Embedded.com
+//                            Copyright 2003 - 2012
+//
+//                         Email: admin@ultra-embedded.com
+//
+//                                License: GPL
+//   If you would like a version with a more permissive license for use in
+//   closed source commercial applications please contact me for details.
+//-----------------------------------------------------------------------------
+//
+// This file is part of FAT File IO Library.
+//
+// FAT File IO Library 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.
+//
+// FAT File IO 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 General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with FAT File IO Library; if not, write to the Free Software
+// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+#include <string.h>
+#include "fat_defs.h"
+#include "fat_access.h"
+#include "fat_table.h"
+#include "fat_write.h"
+#include "fat_string.h"
+#include "fat_misc.h"
+
+#if FATFS_INC_WRITE_SUPPORT
+//-----------------------------------------------------------------------------
+// fatfs_add_free_space: Allocate another cluster of free space to the end
+// of a files cluster chain.
+//-----------------------------------------------------------------------------
+int fatfs_add_free_space(struct fatfs *fs, uint32 *startCluster, uint32 clusters)
+{
+    uint32 i;
+    uint32 nextcluster;
+    uint32 start = *startCluster;
+
+    // Set the next free cluster hint to unknown
+    if (fs->next_free_cluster != FAT32_LAST_CLUSTER)
+        fatfs_set_fs_info_next_free_cluster(fs, FAT32_LAST_CLUSTER);
+
+    for (i=0;i<clusters;i++)
+    {
+        // Start looking for free clusters from the beginning
+        if (fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &nextcluster))
+        {
+            // Point last to this
+            fatfs_fat_set_cluster(fs, start, nextcluster);
+
+            // Point this to end of file
+            fatfs_fat_set_cluster(fs, nextcluster, FAT32_LAST_CLUSTER);
+
+            // Adjust argument reference
+            start = nextcluster;
+            if (i == 0)
+                *startCluster = nextcluster;
+        }
+        else
+            return 0;
+    }
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_allocate_free_space: Add an ammount of free space to a file either from
+// 'startCluster' if newFile = false, or allocating a new start to the chain if
+// newFile = true.
+//-----------------------------------------------------------------------------
+int fatfs_allocate_free_space(struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size)
+{
+    uint32 clusterSize;
+    uint32 clusterCount;
+    uint32 nextcluster;
+
+    if (size==0)
+        return 0;
+
+    // Set the next free cluster hint to unknown
+    if (fs->next_free_cluster != FAT32_LAST_CLUSTER)
+        fatfs_set_fs_info_next_free_cluster(fs, FAT32_LAST_CLUSTER);
+
+    // Work out size and clusters
+    clusterSize = fs->sectors_per_cluster * FAT_SECTOR_SIZE;
+    clusterCount = (size / clusterSize);
+
+    // If any left over
+    if (size-(clusterSize*clusterCount))
+        clusterCount++;
+
+    // Allocated first link in the chain if a new file
+    if (newFile)
+    {
+        if (!fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &nextcluster))
+            return 0;
+
+        // If this is all that is needed then all done
+        if (clusterCount==1)
+        {
+            fatfs_fat_set_cluster(fs, nextcluster, FAT32_LAST_CLUSTER);
+            *startCluster = nextcluster;
+            return 1;
+        }
+    }
+    // Allocate from end of current chain (startCluster is end of chain)
+    else
+        nextcluster = *startCluster;
+
+    if (!fatfs_add_free_space(fs, &nextcluster, clusterCount))
+            return 0;
+
+    return 1;
+}
+//-----------------------------------------------------------------------------
+// fatfs_find_free_dir_offset: Find a free space in the directory for a new entry
+// which takes up 'entryCount' blocks (or allocate some more)
+//-----------------------------------------------------------------------------
+static int fatfs_find_free_dir_offset(struct fatfs *fs, uint32 dirCluster, int entryCount, uint32 *pSector, uint8 *pOffset)
+{
+    struct fat_dir_entry *directoryEntry;
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    uint8 i=0;
+    int x=0;
+    int possible_spaces = 0;
+    int start_recorded = 0;
+
+    // No entries required?
+    if (entryCount == 0)
+        return 0;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, dirCluster, x++, 0))
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // Overlay directory entry over buffer
+                directoryEntry = (struct fat_dir_entry*)(fs->currentsector.sector+recordoffset);
+
+                // LFN Entry
+                if (fatfs_entry_lfn_text(directoryEntry))
+                {
+                    // First entry?
+                    if (possible_spaces == 0)
+                    {
+                        // Store start
+                        *pSector = x-1;
+                        *pOffset = item;
+                        start_recorded = 1;
+                    }
+
+                    // Increment the count in-case the file turns
+                    // out to be deleted...
+                    possible_spaces++;
+                }
+                // SFN Entry
+                else
+                {
+                    // Has file been deleted?
+                    if (fs->currentsector.sector[recordoffset] == FILE_HEADER_DELETED)
+                    {
+                        // First entry?
+                        if (possible_spaces == 0)
+                        {
+                            // Store start
+                            *pSector = x-1;
+                            *pOffset = item;
+                            start_recorded = 1;
+                        }
+
+                        possible_spaces++;
+
+                        // We have found enough space?
+                        if (possible_spaces >= entryCount)
+                            return 1;
+
+                        // Else continue counting until we find a valid entry!
+                    }
+                    // Is the file entry empty?
+                    else if (fs->currentsector.sector[recordoffset] == FILE_HEADER_BLANK)
+                    {
+                        // First entry?
+                        if (possible_spaces == 0)
+                        {
+                            // Store start
+                            *pSector = x-1;
+                            *pOffset = item;
+                            start_recorded = 1;
+                        }
+
+                        // Increment the blank entries count
+                        possible_spaces++;
+
+                        // We have found enough space?
+                        if (possible_spaces >= entryCount)
+                            return 1;
+                    }
+                    // File entry is valid
+                    else
+                    {
+                        // Reset all flags
+                        possible_spaces = 0;
+                        start_recorded = 0;
+                    }
+                }
+            } // End of for
+        } // End of if
+        // Run out of free space in the directory, allocate some more
+        else
+        {
+            uint32 newCluster;
+
+            // Get a new cluster for directory
+            if (!fatfs_find_blank_cluster(fs, fs->rootdir_first_cluster, &newCluster))
+                return 0;
+
+            // Add cluster to end of directory tree
+            if (!fatfs_fat_add_cluster_to_chain(fs, dirCluster, newCluster))
+                return 0;
+
+            // Erase new directory cluster
+            memset(fs->currentsector.sector, 0x00, FAT_SECTOR_SIZE);
+            for (i=0;i<fs->sectors_per_cluster;i++)
+            {
+                if (!fatfs_write_sector(fs, newCluster, i, 0))
+                    return 0;
+            }
+
+            // If non of the name fitted on previous sectors
+            if (!start_recorded)
+            {
+                // Store start
+                *pSector = (x-1);
+                *pOffset = 0;
+                start_recorded = 1;
+            }
+
+            return 1;
+        }
+    } // End of while loop
+
+    return 0;
+}
+//-----------------------------------------------------------------------------
+// fatfs_add_file_entry: Add a directory entry to a location found by FindFreeOffset
+//-----------------------------------------------------------------------------
+int fatfs_add_file_entry(struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir)
+{
+    uint8 item=0;
+    uint16 recordoffset = 0;
+    uint8 i=0;
+    uint32 x=0;
+    int entryCount;
+    struct fat_dir_entry shortEntry;
+    int dirtySector = 0;
+
+    uint32 dirSector = 0;
+    uint8 dirOffset = 0;
+    int foundEnd = 0;
+
+    uint8 checksum;
+    uint8 *pSname;
+
+    // No write access?
+    if (!fs->disk_io.write_media)
+        return 0;
+
+#if FATFS_INC_LFN_SUPPORT
+    // How many LFN entries are required?
+    // NOTE: We always request one LFN even if it would fit in a SFN!
+    entryCount = fatfs_lfn_entries_required(filename);
+    if (!entryCount)
+        return 0;
+#else
+    entryCount = 0;
+#endif
+
+    // Find space in the directory for this filename (or allocate some more)
+    // NOTE: We need to find space for at least the LFN + SFN (or just the SFN if LFNs not supported).
+    if (!fatfs_find_free_dir_offset(fs, dirCluster, entryCount + 1, &dirSector, &dirOffset))
+        return 0;
+
+    // Generate checksum of short filename
+    pSname = (uint8*)shortfilename;
+    checksum = 0;
+    for (i=11; i!=0; i--) checksum = ((checksum & 1) ? 0x80 : 0) + (checksum >> 1) + *pSname++;
+
+    // Start from current sector where space was found!
+    x = dirSector;
+
+    // Main cluster following loop
+    while (1)
+    {
+        // Read sector
+        if (fatfs_sector_reader(fs, dirCluster, x++, 0))
+        {
+            // Analyse Sector
+            for (item = 0; item < FAT_DIR_ENTRIES_PER_SECTOR; item++)
+            {
+                // Create the multiplier for sector access
+                recordoffset = FAT_DIR_ENTRY_SIZE * item;
+
+                // If the start position for the entry has been found
+                if (foundEnd==0)
+                    if ( (dirSector==(x-1)) && (dirOffset==item) )
+                        foundEnd = 1;
+
+                // Start adding filename
+                if (foundEnd)
+                {
+                    if (entryCount==0)
+                    {
+                        // Short filename
+                        fatfs_sfn_create_entry(shortfilename, size, startCluster, &shortEntry, dir);
+
+#if FATFS_INC_TIME_DATE_SUPPORT
+                        // Update create, access & modify time & date
+                        fatfs_update_timestamps(&shortEntry, 1, 1, 1);
+#endif
+
+                        memcpy(&fs->currentsector.sector[recordoffset], &shortEntry, sizeof(shortEntry));
+
+                        // Writeback
+                        return fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1);
+                    }
+#if FATFS_INC_LFN_SUPPORT
+                    else
+                    {
+                        entryCount--;
+
+                        // Copy entry to directory buffer
+                        fatfs_filename_to_lfn(filename, &fs->currentsector.sector[recordoffset], entryCount, checksum);
+                        dirtySector = 1;
+                    }
+#endif
+                }
+            } // End of if
+
+            // Write back to disk before loading another sector
+            if (dirtySector)
+            {
+                if (!fs->disk_io.write_media(fs->currentsector.address, fs->currentsector.sector, 1))
+                    return 0;
+
+                dirtySector = 0;
+            }
+        }
+        else
+            return 0;
+    } // End of while loop
+
+    return 0;
+}
+#endif
diff --git a/vtoyjump/vtoyjump/fat_io_lib/fat_write.h b/vtoyjump/vtoyjump/fat_io_lib/fat_write.h
new file mode 100644 (file)
index 0000000..5558a86
--- /dev/null
@@ -0,0 +1,14 @@
+#ifndef __FAT_WRITE_H__
+#define __FAT_WRITE_H__
+
+#include "fat_defs.h"
+#include "fat_opts.h"
+
+//-----------------------------------------------------------------------------
+// Prototypes
+//-----------------------------------------------------------------------------
+int fatfs_add_file_entry(struct fatfs *fs, uint32 dirCluster, char *filename, char *shortfilename, uint32 startCluster, uint32 size, int dir);
+int fatfs_add_free_space(struct fatfs *fs, uint32 *startCluster, uint32 clusters);
+int fatfs_allocate_free_space(struct fatfs *fs, int newFile, uint32 *startCluster, uint32 size);
+
+#endif
diff --git a/vtoyjump/vtoyjump/fat_io_lib/version.txt b/vtoyjump/vtoyjump/fat_io_lib/version.txt
new file mode 100644 (file)
index 0000000..bc02b86
--- /dev/null
@@ -0,0 +1 @@
+2.6.11
diff --git a/vtoyjump/vtoyjump/vtoyjump.c b/vtoyjump/vtoyjump/vtoyjump.c
new file mode 100644 (file)
index 0000000..221de48
--- /dev/null
@@ -0,0 +1,874 @@
+/******************************************************************************
+* vtoyjump.c
+*
+* Copyright (c) 2020, longpanda <admin@ventoy.net>
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License as
+* published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <Windows.h>
+#include <virtdisk.h>
+#include <winioctl.h>
+#include <VersionHelpers.h>
+#include "vtoyjump.h"
+#include "fat_filelib.h"
+
+static BOOL g_64bit_system = FALSE;
+static ventoy_guid g_ventoy_guid = VENTOY_GUID;
+
+void Log(const char *Fmt, ...)
+{
+       va_list Arg;
+       int Len = 0;
+       FILE *File = NULL;
+       SYSTEMTIME Sys;
+       char szBuf[1024];
+
+       GetLocalTime(&Sys);
+       Len += sprintf_s(szBuf, sizeof(szBuf),
+               "[%4d/%02d/%02d %02d:%02d:%02d.%03d] ",
+               Sys.wYear, Sys.wMonth, Sys.wDay,
+               Sys.wHour, Sys.wMinute, Sys.wSecond,
+               Sys.wMilliseconds);
+
+       va_start(Arg, Fmt);
+       Len += vsnprintf_s(szBuf + Len, sizeof(szBuf)-Len, sizeof(szBuf)-Len, Fmt, Arg);
+       va_end(Arg);
+
+       fopen_s(&File, "ventoy.log", "a+");
+       if (File)
+       {
+               fwrite(szBuf, 1, Len, File);
+               fwrite("\n", 1, 1, File);
+               fclose(File);
+       }
+}
+
+
+static int LoadNtDriver(const char *DrvBinPath)
+{
+       int i;
+       int rc = 0;
+       BOOL Ret;
+       DWORD Status;
+       SC_HANDLE hServiceMgr;
+       SC_HANDLE hService;
+       char name[256] = { 0 };
+
+       for (i = (int)strlen(DrvBinPath) - 1; i >= 0; i--)
+       {
+               if (DrvBinPath[i] == '\\' || DrvBinPath[i] == '/')
+               {
+                       sprintf_s(name, sizeof(name), "%s", DrvBinPath + i + 1);
+                       break;
+               }
+       }
+
+       Log("Load NT driver: %s %s", DrvBinPath, name);
+
+       hServiceMgr = OpenSCManagerA(NULL, NULL, SC_MANAGER_ALL_ACCESS);
+       if (hServiceMgr == NULL)
+       {
+               Log("OpenSCManager failed Error:%u", GetLastError());
+               return 1;
+       }
+
+       Log("OpenSCManager OK");
+
+       hService = CreateServiceA(hServiceMgr,
+               name,
+               name,
+               SERVICE_ALL_ACCESS,
+               SERVICE_KERNEL_DRIVER,
+               SERVICE_DEMAND_START,
+               SERVICE_ERROR_NORMAL,
+               DrvBinPath,
+               NULL, NULL, NULL, NULL, NULL);
+       if (hService == NULL)
+       {
+               Status = GetLastError();
+               if (Status != ERROR_IO_PENDING && Status != ERROR_SERVICE_EXISTS)
+               {
+                       Log("CreateService failed v %u", Status);
+                       CloseServiceHandle(hServiceMgr);
+                       return 1;
+               }
+
+               hService = OpenServiceA(hServiceMgr, name, SERVICE_ALL_ACCESS);
+               if (hService == NULL)
+               {
+                       Log("OpenService failed %u", Status);
+                       CloseServiceHandle(hServiceMgr);
+                       return 1;
+               }
+       }
+
+       Log("CreateService imdisk OK");
+
+       Ret = StartServiceA(hService, 0, NULL);
+       if (Ret)
+       {
+               Log("StartService OK");
+       }
+       else
+       {
+               Status = GetLastError();
+               if (Status == ERROR_SERVICE_ALREADY_RUNNING)
+               {
+                       rc = 0;
+               }
+               else
+               {
+                       Log("StartService error  %u", Status);
+                       rc = 1;
+               }
+       }
+
+       CloseServiceHandle(hService);
+       CloseServiceHandle(hServiceMgr);
+
+       Log("Load NT driver %s", rc ? "failed" : "success");
+
+       return rc;
+}
+
+static int ReadWholeFile2Buf(const char *Fullpath, void **Data, DWORD *Size)
+{
+       int rc = 1;
+       DWORD FileSize;
+       DWORD dwSize;
+       HANDLE Handle;
+       BYTE *Buffer = NULL;
+
+       Log("ReadWholeFile2Buf <%s>", Fullpath);
+
+       Handle = CreateFileA(Fullpath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
+       if (Handle == INVALID_HANDLE_VALUE)
+       {
+               Log("Could not open the file<%s>, error:%u", Fullpath, GetLastError());
+               goto End;
+       }
+
+       FileSize = SetFilePointer(Handle, 0, NULL, FILE_END);
+
+       Buffer = malloc(FileSize);
+       if (!Buffer)
+       {
+               Log("Failed to alloc memory size:%u", FileSize);
+               goto End;
+       }
+
+       SetFilePointer(Handle, 0, NULL, FILE_BEGIN);
+       if (!ReadFile(Handle, Buffer, FileSize, &dwSize, NULL))
+       {
+               Log("ReadFile failed, dwSize:%u  error:%u", dwSize, GetLastError());
+               goto End;
+       }
+
+       *Data = Buffer;
+       *Size = FileSize;
+
+       Log("Success read file size:%u", FileSize);
+
+       rc = 0;
+
+End:
+       SAFE_CLOSE_HANDLE(Handle);
+
+       return rc;
+}
+
+static BOOL CheckPeHead(BYTE *Head)
+{
+       UINT32 PeOffset;
+
+       if (Head[0] != 'M' || Head[1] != 'Z')
+       {
+               return FALSE;
+       }
+
+       PeOffset = *(UINT32 *)(Head + 60);
+       if (*(UINT32 *)(Head + PeOffset) != 0x00004550)
+       {
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static BOOL IsPe64(BYTE *buffer)
+{
+       DWORD pe_off;
+
+       if (!CheckPeHead(buffer))
+       {
+               return FALSE;
+       }
+
+       pe_off = *(UINT32 *)(buffer + 60);
+       if (*(UINT16 *)(buffer + pe_off + 24) == 0x020b)
+       {
+               return TRUE;
+       }
+
+       return FALSE;
+}
+
+
+static BOOL CheckOsParam(ventoy_os_param *param)
+{
+       UINT32 i;
+       BYTE Sum = 0;
+
+       if (memcmp(&param->guid, &g_ventoy_guid, sizeof(ventoy_guid)))
+       {
+               return FALSE;
+       }
+
+       for (i = 0; i < sizeof(ventoy_os_param); i++)
+       {
+               Sum += *((BYTE *)param + i);
+       }
+       
+       if (Sum)
+       {
+               return FALSE;
+       }
+
+       if (param->vtoy_img_location_addr % 4096)
+       {
+               return FALSE;
+       }
+
+       return TRUE;
+}
+
+static int SaveBuffer2File(const char *Fullpath, void *Buffer, DWORD Length)
+{
+       int rc = 1;
+       DWORD dwSize;
+       HANDLE Handle;
+
+       Log("SaveBuffer2File <%s> len:%u", Fullpath, Length);
+
+       Handle = CreateFileA(Fullpath, GENERIC_READ | GENERIC_WRITE,
+               FILE_SHARE_READ | FILE_SHARE_WRITE, 0, CREATE_NEW, 0, 0);
+       if (Handle == INVALID_HANDLE_VALUE)
+       {
+               Log("Could not create new file, error:%u", GetLastError());
+               goto End;
+       }
+
+       WriteFile(Handle, Buffer, Length, &dwSize, NULL);
+
+       rc = 0;
+
+End:
+       SAFE_CLOSE_HANDLE(Handle);
+
+       return rc;
+}
+
+static BOOL IsPathExist(BOOL Dir, const char *Fmt, ...)
+{
+       va_list Arg;
+       HANDLE hFile;
+       DWORD Attr;
+       CHAR FilePath[MAX_PATH];
+
+       va_start(Arg, Fmt);
+       vsnprintf_s(FilePath, sizeof(FilePath), sizeof(FilePath), Fmt, Arg);
+       va_end(Arg);
+
+       hFile = CreateFileA(FilePath, FILE_READ_EA, FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
+       if (INVALID_HANDLE_VALUE == hFile)
+       {
+               return FALSE;
+       }
+
+       CloseHandle(hFile);
+
+       Attr = GetFileAttributesA(FilePath);
+
+       if (Dir)
+       {
+               if ((Attr & FILE_ATTRIBUTE_DIRECTORY) == 0)
+               {
+                       return FALSE;
+               }
+       }
+       else
+       {
+               if (Attr & FILE_ATTRIBUTE_DIRECTORY)
+               {
+                       return FALSE;
+               }
+       }
+
+       return TRUE;
+}
+
+static int GetPhyDiskUUID(const char LogicalDrive, UINT8 *UUID, DISK_EXTENT *DiskExtent)
+{
+       BOOL Ret;
+       DWORD dwSize;
+       HANDLE Handle;
+       VOLUME_DISK_EXTENTS DiskExtents;
+       CHAR PhyPath[128];
+       UINT8 SectorBuf[512];
+
+       Log("GetPhyDiskUUID %C", LogicalDrive);
+
+       sprintf_s(PhyPath, sizeof(PhyPath), "\\\\.\\%C:", LogicalDrive);
+       Handle = CreateFileA(PhyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
+       if (Handle == INVALID_HANDLE_VALUE)
+       {
+               Log("Could not open the disk<%s>, error:%u", PhyPath, GetLastError());
+               return 1;
+       }
+
+       Ret = DeviceIoControl(Handle,
+               IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
+               NULL,
+               0,
+               &DiskExtents,
+               (DWORD)(sizeof(DiskExtents)),
+               (LPDWORD)&dwSize,
+               NULL);
+       if (!Ret || DiskExtents.NumberOfDiskExtents == 0)
+       {
+               Log("DeviceIoControl IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS failed, error:%u", GetLastError());
+               CloseHandle(Handle);
+               return 1;
+       }
+       CloseHandle(Handle);
+
+       memcpy(DiskExtent, DiskExtents.Extents, sizeof(DiskExtent));
+       Log("%C: is in PhysicalDrive%d ", LogicalDrive, DiskExtents.Extents[0].DiskNumber);
+
+       sprintf_s(PhyPath, sizeof(PhyPath), "\\\\.\\PhysicalDrive%d", DiskExtents.Extents[0].DiskNumber);
+       Handle = CreateFileA(PhyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
+       if (Handle == INVALID_HANDLE_VALUE)
+       {
+               Log("Could not open the disk<%s>, error:%u", PhyPath, GetLastError());
+               return 1;
+       }
+
+       if (!ReadFile(Handle, SectorBuf, sizeof(SectorBuf), &dwSize, NULL))
+       {
+               Log("ReadFile failed, dwSize:%u  error:%u", dwSize, GetLastError());
+               CloseHandle(Handle);
+               return 1;
+       }
+       
+       memcpy(UUID, SectorBuf + 0x180, 16);
+       CloseHandle(Handle);
+       return 0;
+}
+
+int VentoyMountISOByAPI(const char *IsoPath)
+{
+       HANDLE Handle;
+       DWORD Status;
+       WCHAR wFilePath[512] = { 0 };
+       VIRTUAL_STORAGE_TYPE StorageType;
+       OPEN_VIRTUAL_DISK_PARAMETERS OpenParameters;
+       ATTACH_VIRTUAL_DISK_PARAMETERS AttachParameters;
+
+       Log("VentoyMountISOByAPI <%s>", IsoPath);
+
+       MultiByteToWideChar(CP_ACP, 0, IsoPath, (int)strlen(IsoPath), wFilePath, (int)(sizeof(wFilePath) / sizeof(WCHAR)));
+
+       memset(&StorageType, 0, sizeof(StorageType));
+       memset(&OpenParameters, 0, sizeof(OpenParameters));
+       memset(&AttachParameters, 0, sizeof(AttachParameters));
+
+       OpenParameters.Version = OPEN_VIRTUAL_DISK_VERSION_1;
+       AttachParameters.Version = ATTACH_VIRTUAL_DISK_VERSION_1;
+
+       Status = OpenVirtualDisk(&StorageType, wFilePath, VIRTUAL_DISK_ACCESS_READ, 0, &OpenParameters, &Handle);
+       if (Status != ERROR_SUCCESS)
+       {
+               if (ERROR_VIRTDISK_PROVIDER_NOT_FOUND == Status)
+               {
+                       Log("VirtualDisk for ISO file is not supported in current system");
+               }
+               else
+               {
+                       Log("Failed to open virtual disk ErrorCode:%u", Status);
+               }
+               return 1;
+       }
+
+       Log("OpenVirtualDisk success");
+
+       Status = AttachVirtualDisk(Handle, NULL, ATTACH_VIRTUAL_DISK_FLAG_READ_ONLY | ATTACH_VIRTUAL_DISK_FLAG_PERMANENT_LIFETIME, 0, &AttachParameters, NULL);
+       if (Status != ERROR_SUCCESS)
+       {
+               Log("Failed to attach virtual disk ErrorCode:%u", Status);
+               CloseHandle(Handle);
+               return 1;
+       }
+
+       CloseHandle(Handle);
+       return 0;
+}
+
+
+static HANDLE g_FatPhyDrive;
+static UINT64 g_Part2StartSec;
+
+static int CopyFileFromFatDisk(const CHAR* SrcFile, const CHAR *DstFile)
+{
+       int rc = 1;
+       int size = 0;
+       char *buf = NULL;
+       void *flfile = NULL;
+
+       Log("CopyFileFromFatDisk (%s)==>(%s)", SrcFile, DstFile);
+
+       flfile = fl_fopen(SrcFile, "rb");
+       if (flfile)
+       {
+               fl_fseek(flfile, 0, SEEK_END);
+               size = (int)fl_ftell(flfile);
+               fl_fseek(flfile, 0, SEEK_SET);
+
+               buf = (char *)malloc(size);
+               if (buf)
+               {
+                       fl_fread(buf, 1, size, flfile);
+
+                       rc = 0;
+                       SaveBuffer2File(DstFile, buf, size);
+                       free(buf);
+               }
+
+               fl_fclose(flfile);
+       }
+
+       return rc;
+}
+
+static int VentoyFatDiskRead(uint32 Sector, uint8 *Buffer, uint32 SectorCount)
+{
+       DWORD dwSize;
+       BOOL bRet;
+       DWORD ReadSize;
+       LARGE_INTEGER liCurrentPosition;
+
+       liCurrentPosition.QuadPart = Sector + g_Part2StartSec;
+       liCurrentPosition.QuadPart *= 512;
+       SetFilePointerEx(g_FatPhyDrive, liCurrentPosition, &liCurrentPosition, FILE_BEGIN);
+
+       ReadSize = (DWORD)(SectorCount * 512);
+
+       bRet = ReadFile(g_FatPhyDrive, Buffer, ReadSize, &dwSize, NULL);
+       if (bRet == FALSE || dwSize != ReadSize)
+       {
+               Log("ReadFile error bRet:%u WriteSize:%u dwSize:%u ErrCode:%u\n", bRet, ReadSize, dwSize, GetLastError());
+       }
+
+       return 1;
+}
+
+static CHAR GetMountLogicalDrive(void)
+{
+       CHAR Letter = 'Z';
+       DWORD Drives;
+       DWORD Mask = 0x2000000;
+
+       Drives = GetLogicalDrives();
+    Log("Drives=0x%x", Drives);
+
+       while (Mask)
+       {
+               if ((Drives & Mask) == 0)
+               {
+                       break;
+               }
+
+               Letter--;
+               Mask >>= 1;
+       }
+
+       return Letter;
+}
+
+int VentoyMountISOByImdisk(const char *IsoPath, DWORD PhyDrive)
+{
+       int rc = 1;
+       BOOL bRet;
+       CHAR Letter;
+       DWORD dwBytes;
+       HANDLE hDrive;
+       CHAR PhyPath[MAX_PATH];
+       STARTUPINFOA Si;
+       PROCESS_INFORMATION Pi;
+       GET_LENGTH_INFORMATION LengthInfo;
+
+       Log("VentoyMountISOByImdisk %s", IsoPath);
+
+       sprintf_s(PhyPath, sizeof(PhyPath), "\\\\.\\PhysicalDrive%d", PhyDrive);
+       hDrive = CreateFileA(PhyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
+       if (hDrive == INVALID_HANDLE_VALUE)
+       {
+               Log("Could not open the disk<%s>, error:%u", PhyPath, GetLastError());
+               goto End;
+       }
+
+       bRet = DeviceIoControl(hDrive, IOCTL_DISK_GET_LENGTH_INFO, NULL, 0, &LengthInfo, sizeof(LengthInfo), &dwBytes, NULL);
+       if (!bRet)
+       {
+               Log("Could not get phy disk %s size, error:%u", PhyPath, GetLastError());
+               goto End;
+       }
+
+       g_FatPhyDrive = hDrive;
+       g_Part2StartSec = (LengthInfo.Length.QuadPart - VENTOY_EFI_PART_SIZE) / 512;
+
+       Log("Parse FAT fs...");
+
+       fl_init();
+
+       if (0 == fl_attach_media(VentoyFatDiskRead, NULL))
+       {
+               if (g_64bit_system)
+               {
+                       CopyFileFromFatDisk("/ventoy/imdisk/64/imdisk.sys", "ventoy\\imdisk.sys");
+                       CopyFileFromFatDisk("/ventoy/imdisk/64/imdisk.exe", "ventoy\\imdisk.exe");
+                       CopyFileFromFatDisk("/ventoy/imdisk/64/imdisk.cpl", "ventoy\\imdisk.cpl");
+               }
+               else
+               {
+                       CopyFileFromFatDisk("/ventoy/imdisk/32/imdisk.sys", "ventoy\\imdisk.sys");
+                       CopyFileFromFatDisk("/ventoy/imdisk/32/imdisk.exe", "ventoy\\imdisk.exe");
+                       CopyFileFromFatDisk("/ventoy/imdisk/32/imdisk.cpl", "ventoy\\imdisk.cpl");
+               }
+               
+               GetCurrentDirectoryA(sizeof(PhyPath), PhyPath);
+               strcat_s(PhyPath, sizeof(PhyPath), "\\ventoy\\imdisk.sys");
+
+               if (LoadNtDriver(PhyPath) == 0)
+               {
+                       rc = 0;
+
+                       Letter = GetMountLogicalDrive();
+            sprintf_s(PhyPath, sizeof(PhyPath), "ventoy\\imdisk.exe -a -o ro -f %s -m %C:", IsoPath, Letter);
+
+                       Log("mount iso to %C: use imdisk cmd <%s>", Letter, PhyPath);
+
+            GetStartupInfoA(&Si);
+
+            Si.dwFlags |= STARTF_USESHOWWINDOW;
+            Si.wShowWindow = SW_HIDE;
+
+                       CreateProcessA(NULL, PhyPath, NULL, NULL, FALSE, 0, NULL, NULL, &Si, &Pi);
+                       WaitForSingleObject(Pi.hProcess, INFINITE);
+               }
+       }
+       fl_shutdown();
+
+End:
+
+       SAFE_CLOSE_HANDLE(hDrive);
+
+       return rc;
+}
+
+static int MountIsoFile(CONST CHAR *IsoPath, DWORD PhyDrive)
+{
+    if (IsWindows8OrGreater())
+    {
+        Log("This is Windows 8 or latter...");
+        if (VentoyMountISOByAPI(IsoPath) == 0)
+        {
+            Log("Mount iso by API success");
+            return 0;
+        }
+        else
+        {
+            Log("Mount iso by API failed, maybe not supported, try imdisk");
+            return VentoyMountISOByImdisk(IsoPath, PhyDrive);
+        }
+    }
+    else
+    {
+        Log("This is before Windows 8 ...");
+        if (VentoyMountISOByImdisk(IsoPath, PhyDrive) == 0)
+        {
+            Log("Mount iso by imdisk success");
+            return 0;
+        }
+        else
+        {
+            return VentoyMountISOByAPI(IsoPath);
+        }
+    }
+}
+
+static int GetPhyDriveByLogicalDrive(int DriveLetter)
+{
+    BOOL Ret;
+    DWORD dwSize;
+    HANDLE Handle;
+    VOLUME_DISK_EXTENTS DiskExtents;
+    CHAR PhyPath[128];
+
+    sprintf_s(PhyPath, sizeof(PhyPath), "\\\\.\\%C:", (CHAR)DriveLetter);
+
+    Handle = CreateFileA(PhyPath, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, 0, OPEN_EXISTING, 0, 0);
+    if (Handle == INVALID_HANDLE_VALUE)
+    {
+        Log("Could not open the disk<%s>, error:%u", PhyPath, GetLastError());
+        return -1;
+    }
+
+    Ret = DeviceIoControl(Handle,
+        IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS,
+        NULL,
+        0,
+        &DiskExtents,
+        (DWORD)(sizeof(DiskExtents)),
+        (LPDWORD)&dwSize,
+        NULL);
+
+    if (!Ret || DiskExtents.NumberOfDiskExtents == 0)
+    {
+        Log("DeviceIoControl IOCTL_VOLUME_GET_VOLUME_DISK_EXTENTS failed %s, error:%u", PhyPath, GetLastError());
+        SAFE_CLOSE_HANDLE(Handle);
+        return -1;
+    }
+    SAFE_CLOSE_HANDLE(Handle);
+
+    Log("LogicalDrive:%s PhyDrive:%d Offset:%llu ExtentLength:%llu",
+        PhyPath,
+        DiskExtents.Extents[0].DiskNumber,
+        DiskExtents.Extents[0].StartingOffset.QuadPart,
+        DiskExtents.Extents[0].ExtentLength.QuadPart
+        );
+
+    return (int)DiskExtents.Extents[0].DiskNumber;
+}
+
+
+static int DeleteVentoyPart2MountPoint(DWORD PhyDrive)
+{
+    CHAR Letter = 'A';
+    DWORD Drives;
+    DWORD PhyDisk;
+    CHAR DriveName[] = "?:\\";
+
+    Log("DeleteVentoyPart2MountPoint Phy%u ...", PhyDrive);
+
+    Drives = GetLogicalDrives();
+    while (Drives)
+    {
+        if ((Drives & 0x01) && IsPathExist(FALSE, "%C:\\ventoy\\ventoy.cpio", Letter))
+        {
+            Log("File %C:\\ventoy\\ventoy.cpio exist", Letter);
+
+            PhyDisk = GetPhyDriveByLogicalDrive(Letter);
+            Log("PhyDisk=%u for %C", PhyDisk, Letter);
+
+            if (PhyDisk == PhyDrive)
+            {
+                DriveName[0] = Letter;
+                DeleteVolumeMountPointA(DriveName);
+                return 0;
+            }
+        }
+
+        Letter++;
+        Drives >>= 1;
+    }
+
+    return 1;
+}
+
+static int VentoyHook(ventoy_os_param *param)
+{
+    int rc;
+       CHAR Letter = 'A';
+       DISK_EXTENT DiskExtent;
+       DWORD Drives = GetLogicalDrives();
+       UINT8 UUID[16];
+       CHAR IsoPath[MAX_PATH];
+
+       Log("Logical Drives=0x%x Path:<%s>", Drives, param->vtoy_img_path);
+
+       while (Drives)
+       {
+               sprintf_s(IsoPath, sizeof(IsoPath), "%C:\\%s", Letter, param->vtoy_img_path);
+               if (IsPathExist(FALSE, "%s", IsoPath))
+               {
+                       Log("File exist under %C:", Letter);
+                       if (GetPhyDiskUUID(Letter, UUID, &DiskExtent) == 0)
+                       {
+                               if (memcmp(UUID, param->vtoy_disk_guid, 16) == 0)
+                               {
+                                       Log("Disk UUID match");
+                                       break;
+                               }
+                       }
+               }
+               else
+               {
+                       Log("File NOT exist under %C:", Letter);
+               }
+
+               Drives >>= 1;
+               Letter++;
+       }
+
+       if (Drives == 0)
+       {
+               Log("Failed to find ISO file");
+               return 1;
+       }
+
+       Log("Find ISO file <%s>", IsoPath);
+    
+    rc = MountIsoFile(IsoPath, DiskExtent.DiskNumber);
+    Log("Mount ISO FILE: %s", rc == 0 ? "SUCCESS" : "FAILED");
+
+    // for protect
+    rc = DeleteVentoyPart2MountPoint(DiskExtent.DiskNumber);
+    Log("Delete ventoy mountpoint: %s", rc == 0 ? "SUCCESS" : "NO NEED");
+
+    return 0;
+}
+
+int VentoyJump(INT argc, CHAR **argv, CHAR *LunchFile)
+{
+       int rc = 1;
+       DWORD Pos;
+       DWORD PeStart;
+    DWORD FileSize;
+       BYTE *Buffer = NULL; 
+       ventoy_os_param os_param;
+       CHAR ExeFileName[MAX_PATH];
+
+       Log("######## VentoyJump ##########");
+       Log("argc = %d argv[0] = <%s>", argc, argv[0]);
+
+       sprintf_s(ExeFileName, sizeof(ExeFileName), "%s", argv[0]);
+       if (!IsPathExist(FALSE, "%s", ExeFileName))
+       {
+               Log("File %s NOT exist, now try %s.exe", ExeFileName, ExeFileName);
+               sprintf_s(ExeFileName, sizeof(ExeFileName), "%s.exe", argv[0]);
+
+               Log("File %s exist ? %s", ExeFileName, IsPathExist(FALSE, "%s", ExeFileName) ? "YES" : "NO");
+       }
+
+       if (ReadWholeFile2Buf(ExeFileName, (void **)&Buffer, &FileSize))
+       {
+               goto End;
+       }
+       
+       g_64bit_system = IsPe64(Buffer);
+
+       if (!IsPathExist(TRUE, "ventoy"))
+       {
+               if (!CreateDirectoryA("ventoy", NULL))
+               {
+                       Log("Failed to create ventoy directory err:%u", GetLastError());
+                       goto End;
+               }
+       }
+
+       for (PeStart = 0; PeStart < FileSize; PeStart += 16)
+       {
+               if (CheckOsParam((ventoy_os_param *)(Buffer + PeStart)) && 
+                       CheckPeHead(Buffer + PeStart + sizeof(ventoy_os_param)))
+               {
+                       Log("Find os pararm at %u", PeStart);
+                       memcpy(&os_param, Buffer + PeStart, sizeof(ventoy_os_param));
+
+                       if (os_param.vtoy_reserved[0] == 1)
+                       {
+                               Log("break here for debug .....");
+                               goto End;
+                       }
+
+                       // convert / to \\   
+                       for (Pos = 0; Pos < sizeof(os_param.vtoy_img_path) && os_param.vtoy_img_path[Pos]; Pos++)
+                       {
+                               if (os_param.vtoy_img_path[Pos] == '/')
+                               {
+                                       os_param.vtoy_img_path[Pos] = '\\';
+                               }
+                       }
+
+                       PeStart += sizeof(ventoy_os_param);
+                       sprintf_s(LunchFile, MAX_PATH, "ventoy\\%s", ExeFileName);
+                       SaveBuffer2File(LunchFile, Buffer + PeStart, FileSize - PeStart);
+                       break;
+               }
+       }
+
+       if (PeStart >= FileSize)
+       {
+               Log("OS param not found");
+               goto End;
+       }
+
+    if (os_param.vtoy_reserved[0] == 2)
+    {
+        Log("skip hook for debug .....");
+        rc = 0;
+        goto End;
+    }
+
+       rc = VentoyHook(&os_param);
+
+End:
+
+       if (Buffer)
+       {
+               free(Buffer);
+       }
+
+       return rc;
+}
+
+int main(int argc, char **argv)
+{
+       CHAR LunchFile[MAX_PATH];
+       STARTUPINFOA Si;
+       PROCESS_INFORMATION Pi;
+
+    GetStartupInfoA(&Si);
+
+       if (VentoyJump(argc, argv, LunchFile) == 0)
+       {
+        Log("Ventoy jump success ...");
+        Si.dwFlags |= STARTF_USESHOWWINDOW;
+        Si.wShowWindow = SW_HIDE;
+       }
+    else
+    {
+        Log("Ventoy jump fail, now lunch cmd...");
+        sprintf_s(LunchFile, sizeof(LunchFile), "%s", "cmd.exe");
+    }
+
+       CreateProcessA(NULL, LunchFile, NULL, NULL, FALSE, 0, NULL, NULL, &Si, &Pi);
+       WaitForSingleObject(Pi.hProcess, INFINITE);
+
+       return 0;
+}
diff --git a/vtoyjump/vtoyjump/vtoyjump.h b/vtoyjump/vtoyjump/vtoyjump.h
new file mode 100644 (file)
index 0000000..ba20270
--- /dev/null
@@ -0,0 +1,81 @@
+/******************************************************************************
+* vtoyjump.h
+*
+* Copyright (c) 2020, longpanda <admin@ventoy.net>
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License as
+* published by the Free Software Foundation; either version 3 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, see <http://www.gnu.org/licenses/>.
+*
+*/
+#ifndef __VTOYJUMP_H__
+#define __VTOYJUMP_H__
+
+#pragma comment( linker, "/subsystem:\"windows\" /entry:\"mainCRTStartup\"" ) 
+
+#define SIZE_1MB   (1024 * 1024)
+#define VENTOY_EFI_PART_SIZE   (32 * SIZE_1MB)
+
+#define VENTOY_GUID { 0x77772020, 0x2e77, 0x6576, { 0x6e, 0x74, 0x6f, 0x79, 0x2e, 0x6e, 0x65, 0x74 }}
+
+#pragma pack(1)
+
+typedef struct ventoy_guid
+{
+       UINT32   data1;
+       UINT16   data2;
+       UINT16   data3;
+       UINT8    data4[8];
+}ventoy_guid;
+
+
+typedef struct ventoy_os_param
+{
+       ventoy_guid  guid;             // VENTOY_GUID
+       UINT8        chksum;           // checksum
+
+       UINT8   vtoy_disk_guid[16];
+       UINT64  vtoy_disk_size;       // disk size in bytes
+       UINT16  vtoy_disk_part_id;    // begin with 1
+       UINT16  vtoy_disk_part_type;  // 0:exfat   1:ntfs  other: reserved
+       char    vtoy_img_path[384];   // It seems to be enough, utf-8 format
+       UINT64  vtoy_img_size;        // image file size in bytes
+
+       /*
+       * Ventoy will write a copy of ventoy_image_location data into runtime memory
+       * this is the physically address and length of that memory.
+       * Address 0 means no such data exist.
+       * Address will be aligned by 4KB.
+       *
+       */
+       UINT64  vtoy_img_location_addr;
+       UINT32  vtoy_img_location_len;
+
+       UINT64  vtoy_reserved[4];     // Internal use by ventoy
+
+       UINT8   reserved[31];
+}ventoy_os_param;
+
+#pragma pack()
+
+
+#define SAFE_CLOSE_HANDLE(handle) \
+{\
+       if (handle != INVALID_HANDLE_VALUE) \
+       {\
+               CloseHandle(handle); \
+               (handle) = INVALID_HANDLE_VALUE; \
+       }\
+}
+
+
+#endif
diff --git a/vtoyjump/vtoyjump/vtoyjump.vcxproj b/vtoyjump/vtoyjump/vtoyjump.vcxproj
new file mode 100644 (file)
index 0000000..a0a67b1
--- /dev/null
@@ -0,0 +1,190 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project DefaultTargets="Build" ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup Label="ProjectConfigurations">
+    <ProjectConfiguration Include="Debug|Win32">
+      <Configuration>Debug</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Debug|x64">
+      <Configuration>Debug</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|Win32">
+      <Configuration>Release</Configuration>
+      <Platform>Win32</Platform>
+    </ProjectConfiguration>
+    <ProjectConfiguration Include="Release|x64">
+      <Configuration>Release</Configuration>
+      <Platform>x64</Platform>
+    </ProjectConfiguration>
+  </ItemGroup>
+  <PropertyGroup Label="Globals">
+    <ProjectGuid>{0C6C9787-1CFD-4F31-B5FF-6FCDBB6F2321}</ProjectGuid>
+    <Keyword>Win32Proj</Keyword>
+    <RootNamespace>vtoyjump</RootNamespace>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>true</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
+    <ConfigurationType>Application</ConfigurationType>
+    <UseDebugLibraries>false</UseDebugLibraries>
+    <PlatformToolset>v120</PlatformToolset>
+    <WholeProgramOptimization>true</WholeProgramOptimization>
+    <CharacterSet>MultiByte</CharacterSet>
+  </PropertyGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
+  <ImportGroup Label="ExtensionSettings">
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
+    <Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
+  </ImportGroup>
+  <PropertyGroup Label="UserMacros" />
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <LinkIncremental>true</LinkIncremental>
+    <TargetName>$(ProjectName)32</TargetName>
+    <IncludePath>$(ProjectDir)\fat_io_lib;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <LinkIncremental>true</LinkIncremental>
+    <TargetName>$(ProjectName)64</TargetName>
+    <IncludePath>$(ProjectDir)\fat_io_lib;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <LinkIncremental>false</LinkIncremental>
+    <TargetName>$(ProjectName)32</TargetName>
+    <IncludePath>$(ProjectDir)\fat_io_lib;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <LinkIncremental>false</LinkIncremental>
+    <TargetName>$(ProjectName)64</TargetName>
+    <IncludePath>$(ProjectDir)\fat_io_lib;$(IncludePath)</IncludePath>
+  </PropertyGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>FATFS_INC_FORMAT_SUPPORT=0;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>VirtDisk.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
+    <ClCompile>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <WarningLevel>Level3</WarningLevel>
+      <Optimization>Disabled</Optimization>
+      <PreprocessorDefinitions>FATFS_INC_FORMAT_SUPPORT=0;WIN32;_DEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <AdditionalDependencies>VirtDisk.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>FATFS_INC_FORMAT_SUPPORT=0;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>VirtDisk.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
+    <ClCompile>
+      <WarningLevel>Level3</WarningLevel>
+      <PrecompiledHeader>
+      </PrecompiledHeader>
+      <Optimization>MaxSpeed</Optimization>
+      <FunctionLevelLinking>true</FunctionLevelLinking>
+      <IntrinsicFunctions>true</IntrinsicFunctions>
+      <PreprocessorDefinitions>FATFS_INC_FORMAT_SUPPORT=0;WIN32;NDEBUG;_CONSOLE;_LIB;%(PreprocessorDefinitions)</PreprocessorDefinitions>
+      <SDLCheck>true</SDLCheck>
+      <RuntimeLibrary>MultiThreaded</RuntimeLibrary>
+    </ClCompile>
+    <Link>
+      <SubSystem>Console</SubSystem>
+      <GenerateDebugInformation>true</GenerateDebugInformation>
+      <EnableCOMDATFolding>true</EnableCOMDATFolding>
+      <OptimizeReferences>true</OptimizeReferences>
+      <AdditionalDependencies>VirtDisk.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;%(AdditionalDependencies)</AdditionalDependencies>
+    </Link>
+  </ItemDefinitionGroup>
+  <ItemGroup>
+    <ClCompile Include="fat_io_lib\fat_access.c" />
+    <ClCompile Include="fat_io_lib\fat_cache.c" />
+    <ClCompile Include="fat_io_lib\fat_filelib.c" />
+    <ClCompile Include="fat_io_lib\fat_format.c" />
+    <ClCompile Include="fat_io_lib\fat_misc.c" />
+    <ClCompile Include="fat_io_lib\fat_string.c" />
+    <ClCompile Include="fat_io_lib\fat_table.c" />
+    <ClCompile Include="fat_io_lib\fat_write.c" />
+    <ClCompile Include="vtoyjump.c" />
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="fat_io_lib\fat_access.h" />
+    <ClInclude Include="fat_io_lib\fat_cache.h" />
+    <ClInclude Include="fat_io_lib\fat_defs.h" />
+    <ClInclude Include="fat_io_lib\fat_filelib.h" />
+    <ClInclude Include="fat_io_lib\fat_format.h" />
+    <ClInclude Include="fat_io_lib\fat_list.h" />
+    <ClInclude Include="fat_io_lib\fat_misc.h" />
+    <ClInclude Include="fat_io_lib\fat_opts.h" />
+    <ClInclude Include="fat_io_lib\fat_string.h" />
+    <ClInclude Include="fat_io_lib\fat_table.h" />
+    <ClInclude Include="fat_io_lib\fat_types.h" />
+    <ClInclude Include="fat_io_lib\fat_write.h" />
+    <ClInclude Include="vtoyjump.h" />
+  </ItemGroup>
+  <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
+  <ImportGroup Label="ExtensionTargets">
+  </ImportGroup>
+</Project>
\ No newline at end of file
diff --git a/vtoyjump/vtoyjump/vtoyjump.vcxproj.filters b/vtoyjump/vtoyjump/vtoyjump.vcxproj.filters
new file mode 100644 (file)
index 0000000..b4bcca1
--- /dev/null
@@ -0,0 +1,87 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <ItemGroup>
+    <Filter Include="源文件">
+      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
+      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    </Filter>
+    <Filter Include="头文件">
+      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
+      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
+    </Filter>
+    <Filter Include="资源文件">
+      <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
+      <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
+    </Filter>
+  </ItemGroup>
+  <ItemGroup>
+    <ClCompile Include="vtoyjump.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="fat_io_lib\fat_access.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="fat_io_lib\fat_cache.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="fat_io_lib\fat_filelib.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="fat_io_lib\fat_format.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="fat_io_lib\fat_misc.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="fat_io_lib\fat_string.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="fat_io_lib\fat_table.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+    <ClCompile Include="fat_io_lib\fat_write.c">
+      <Filter>源文件</Filter>
+    </ClCompile>
+  </ItemGroup>
+  <ItemGroup>
+    <ClInclude Include="vtoyjump.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_access.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_cache.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_defs.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_filelib.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_format.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_list.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_misc.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_opts.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_string.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_table.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_types.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+    <ClInclude Include="fat_io_lib\fat_write.h">
+      <Filter>头文件</Filter>
+    </ClInclude>
+  </ItemGroup>
+</Project>
\ No newline at end of file
diff --git a/vtoyjump/vtoyjump/vtoyjump.vcxproj.user b/vtoyjump/vtoyjump/vtoyjump.vcxproj.user
new file mode 100644 (file)
index 0000000..ef5ff2a
--- /dev/null
@@ -0,0 +1,4 @@
+<?xml version="1.0" encoding="utf-8"?>
+<Project ToolsVersion="12.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
+  <PropertyGroup />
+</Project>
\ No newline at end of file